<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Справка по языку Релкон</title>
    <style>
        #header {
            background-color:black;
            color:white;
            text-align:center;
            padding:5px;
        }
        #content {
            float:left;
            padding:10px; 
        }
    </style>
</head>

<body>
    <div id="header">
        <h1>Справка по языку Релкон</h1>
    </div>


    <div id="content">
        <big style="font-weight: bold;"><big>Оглавление</big></big>
        <br><br>
        <a href="#a1"><span style="font-weight: bold;">СТРУКТУРА ПРОГРАММЫ</span></a>
        <br style="font-weight: bold;"><a href="#a2"><span style="font-weight: bold;">ПЕРЕМЕННЫЕ И ДАНННЫЕ</span></a>
        <br style="font-weight: bold;"><a href="#a3"><span style="font-weight: bold;">АРИФМЕТИЧЕСКИЕ И ЛОГИЧЕСКИЕ ОПЕРАЦИИ. ОПЕРАТОРЫ ВЫБОРА</span></a>
        <br style="font-weight: bold;"><a href="#a4"><span style="font-weight: bold;">ОРГАНИЗАЦИЯ ЦИКЛОВ</span></a>
        <br style="font-weight: bold;"><a href="#a5"><span style="font-weight: bold;">ОБЪЯВЛЕНИЕ ПРОЦЕССОВ</span></a>
        <br style="font-weight: bold;"><a href="#a6"><span style="font-weight: bold;">ОБЪЯВЛЕНИЕ СИТУАЦИЙ</span></a>
        <br style="font-weight: bold;"><a href="#a7"><span style="font-weight: bold;">РАБОТА С ПУЛЬТАМИ</span></a>
        <br style="font-weight: bold;"><a href="#a8"><span style="font-weight: bold;">ДИСКРЕТНЫЕ И АНАЛОГОВЫЕ ВХОДЫ/ВЫХОДЫ</span></a>
        <br style="font-weight: bold;"><a href="#a9"><span style="font-weight: bold;">МОДУЛИ ВВОДА/ВЫВОДА СЕРИИ MATCHBOX</span></a>
        <br style="font-weight: bold;"><a href="#a10"><span style="font-weight: bold;">СИСТЕМНОЕ ВРЕМЯ КОНТРОЛЛЕРА</span></a>
        <br style="font-weight: bold;"><a href="#a11"><span style="font-weight: bold;">РАБОТА С FRAM</span></a>
        <br style="font-weight: bold;"><a href="#a12"><span style="font-weight: bold;">ЗАВОДСКИЕ УСТАНОВКИ</span></a>
        <br style="font-weight: bold;"><a href="#a13"><span style="font-weight: bold;">ПАМЯТЬ ПОЛЬЗОВАТЕЛЯ</span></a>
        <br style="font-weight: bold;"><a href="#a13_2"><span style="font-weight: bold;">РАСШИРЕННАЯ ПАМЯТЬ ПОЛЬЗОВАТЕЛЯ</span></a>
        <br style="font-weight: bold;"><a href="#a14"><span style="font-weight: bold;">КОНТРОЛЬ ЗАГРУЗКИ ПРОЦЕССОРА</span></a>
        <br style="font-weight: bold;"><a href="#a15"><span style="font-weight: bold;">ЗАРЕЗЕРВИРОВАННЫЕ СЛОВА</span></a>
        <br style="font-weight: bold;"><a href="#a16"><span style="font-weight: bold;">ПЕРЕСЧЁТ ДАННЫХ АЦП</span></a>
        <br style="font-weight: bold;"><a href="#a17"><span style="font-weight: bold;">РАБОТА С SD КАРТОЙ ПАМЯТИ</span></a>
        <br style="font-weight: bold;"><a href="#a18"><span style="font-weight: bold;">РАБОТА С ИНТЕРФЕЙСОМ RS485</span></a>
        <br style="font-weight: bold;"><a href="#a19"><span style="font-weight: bold;">МЕЖКОНТРОЛЛЕРНЫЙ ОБМЕН</span></a>
        <br style="font-weight: bold;"><a href="#a20"><span style="font-weight: bold;">ИНТЕРФЕЙСНЫЕ ПРОТОКОЛЫ</span></a>
        <br style="font-weight: bold;"><span style="font-weight: bold;">    <a href="#a21"><span style="font-style: italic;">Протокол RKBIN</span></a></span>
        <br style="font-weight: bold; font-style: italic;"><span style="font-weight: bold; font-style: italic;">    <a href="#a22">Протокол RKASCII</a></span>
        <br style="font-weight: bold; font-style: italic;"><span style="font-weight: bold; font-style: italic;">    <a href="#a23">Поддерживаемые функции Modbus RTU, Modbus ASCII и Modbus TCP</a></span>
        <br style="font-weight: bold; font-style: italic;"><span style="font-weight: bold; font-style: italic;">    <a target="_top" href="#a24">Алгоритм расчёта контрольной суммы</a></span>
        <br><h4><span style="font-weight: bold;"><a name="a1"></a>СТРУКТУРА ПРОГРАММЫ</span></h4>
        <p> С точки зрения пользователя программа представляет собой набор параллельно работающих процессов. Каждый процесс разбивается на ситуации. Активной (выполняемой в данный момент времени) внутри процесса может быть только одна ситуация. Условия перехода между ситуациями формируются программой. Периодичность выполнения ситуаций и время перехода между ними гарантируется операционной системой контроллера и может составлять 1,5,10,100 мс.</p>    
        <p>Программа, написанная на языке Релкон (создан на базе Си), делится на 3 группы:
            <li> объявление переменных</li>
            <li> инициализация</li>
            <li> исполняемая часть программы</li>
        </p>    
        <p>Переменные проекта объявляются между кодовых строк «#DATA» и «#INIT». Все объявленные здесь переменные являются глобальными и доступны для всех процессов. В этом поле возможно также объявление массивов, структур и макросов.     При необходимости в поле инициализации можно присвоить объявленным ранее переменным стартовые значения. Здесь же определяются номера процессов, которые будут запущены при старте.<br>    В последней группе располагается непосредственно текст программы.</p>    
        <p>В отличии от языка «Си», «Релкон» не позволяет пользователю создавать собственные функции. Частично их заменяют ситуации. Ниже приведён пример, иллюстрирующий описанную структуру .</p>
        <pre>
            <span style="color: rgb(153, 0, 0);">#DATA</span>    
            <span style="color: rgb(0, 153, 0);">// Объявление переменных</span>      
            <span style="color: rgb(0, 0, 153);">   #define</span> AR_SIZE 100   
            <span style="color: rgb(0, 0, 153);">   unsigned char</span> my_var; 
            <span style="color: rgb(0, 0, 153);">   unsigned short</span> my_array[AR_SIZE];  
            <span style="color: rgb(153, 0, 0);">#INIT</span>  
            <span style="color: rgb(0, 153, 0);">// Инициализация</span>  
                my_var=0;       
            <span style="color: rgb(153, 0, 0);">#STARTp0;</span>      
            <span style="color: rgb(0, 153, 0);">// Начало программы</span>
            <span style="color: rgb(153, 0, 0);">#PROCESS 0</span> 
            <span style="color: rgb(153, 0, 0);">#SIT1(0.01)</span>     
                my_array[my_var]=my_var;     
                my_var=my_var+1;     
                if(my_var&gt;=AR_SIZE) my_var=0;   
            <span style="color: rgb(153, 0, 0);"> #/R;</span>
        </pre>
        <p><span style="font-weight: bold;"><a name="a2"></a>ПЕРЕМЕННЫЕ И ДАНННЫЕ</span></p>
        <p>
            Все переменные делятся на 2 типа: системные и пользовательские. Системные переменные входят в состав операционной системы контроллера и не требуют объявления в разделе «<span style="font-weight: bold; font-style: italic;">DATA</span>». К ним относятся дискретные и аналоговые входы/выходы контроллера, переменные для работы с пультом, системные время и дата, заводские установки, счётчики ошибок модулей ввода/вывода серии <span style="font-style: italic; font-weight: bold;">MATCHBOX</span>. Более подробно они описаны в соответствующих разделах. Пользовательские переменные создаются программистом<br>самостоятельно.<br>
        </p>    
        <p>
            Память контроллера разделяется на типы:<br>
            <ul> 
                <li>   flash память программы</li>
                <li>   оперативная память</li>
                <li>   fram память</li>
                <li>   память пользователя</li>
                <li>   SD карта</li>
            </ul> 
        </p>
        <p>
            Во <span style="font-weight: bold; font-style: italic;">flash</span> памяти хранятся константы и выполняемая программа. Содержимое этой памяти не может быть изменено программой контроллера.
        </p>
        <p>
            В <span style="font-weight: bold;">оперативной</span> памяти хранятся все переменные, объявленные пользователем. При пропадании питания контроллера все данные, хранящиеся в ней, сбрасываются.<br>    
        </p>
        <p>
            <span style="font-weight: bold;">FRAM</span> память — энергонезависимая память. Может служить для хранения настроек работы автоматики, ведения архивов. Этот тип памяти более медленный по сравнению с оперативной, но её содержимое сохраняется даже при пропадании питания. Часть этой памяти зарезервирована операционной системой для хранения параметров контроллера и заводских установок.<br>    
        </p>
        <p>
            Основное назначение SD карты — хранение больших объёмов архивных данных. Этот тип памяти существенно медленнее по сравнению с <span style="font-style: italic; font-weight: bold;">FRAM</span> памятью.
        </p>
        <p>
            <span style="font-weight: bold;">Память пользователя</span> представляет собой фрагмент оперативной памяти, зарезервированной контроллером. Её основное назначение — хранение данных, предназначенных для организации диспетчеризации. Переменные, хранящиеся в обычной оперативной памяти, могут менять свои адреса после компиляции проекта. Для того чтобы не настраивать адреса переменных, с которыми работает система диспетчеризации, каждый раз после изменения проекта и служит память пользователя.
        </p>
        <p>
            В оперативной памяти могут быть объявлены переменные следующих типов:<br><br>
            <table style="text-align: left; width: 546px; height: 228px;" border="1" cellpadding="2" cellspacing="2">
                <tbody>
                    <tr><td><span style="font-weight: bold;">название</span></td><td><span style="font-weight: bold;">описание</span></td></tr>
                    <tr><td><span style="color: rgb(0, 0, 153);">unsigned char</span></td><td>однобайтовая беззнаковая</td></tr>
                    <tr><td><span style="color: rgb(0, 0, 153);">char</span></td><td>однобайтовая знаковая</td></tr>
                    <tr><td style="color: rgb(0, 0, 153);">unsigned short</td><td>двухбайтовая беззнаковая</td></tr>
                    <tr><td style="color: rgb(0, 0, 153);">short</td><td>двухбайтовая знаковая</td></tr>
                    <tr><td style="color: rgb(0, 0, 153);">unsigned long</td><td>четырёхбайтовая беззнаковая</td></tr>
                    <tr><td style="color: rgb(0, 0, 153);">long</td><td>четырёхбайтовая знаковая</td></tr>
                    <tr><td style="color: rgb(0, 0, 153);">float</td><td>четырёхбайтовая с плавающей запятой</td></tr>
                </tbody>
            </table>
        </p>
        <p><span style="font-weight: bold;"><br><br><a name="a3"></a>АРИФМЕТИЧЕСКИЕ И ЛОГИЧЕСКИЕ ОПЕРАЦИИ. ОПЕРАТОРЫ ВЫБОРА.</span></p>
        <p>
            <span style="font-weight: bold;">Арифметические операции :</span>
            <ol>
                <li>сложение <span style="color: rgb(0, 0, 153);">a=b+c;</span></li>
                <li>вычитание <span style="color: rgb(0, 0, 153);">a=b-c;</span></li>
                <li>умножение <span style="color: rgb(0, 0, 153);">a=b*c;</span></li>
                <li>деление <span style="color: rgb(0, 0, 153);">a=b/c;</span></li>
                <li>деление по модулю1 <span style="color: rgb(0, 0, 153);">a=b%c;</span></li>
                <li>пост инкремент <span style="color: rgb(0, 0, 153);">a++;</span> (аналогично <span style="color: rgb(0, 0, 153);">a=a+1;</span>)</li>
                <li>пост декремент <span style="color: rgb(0, 0, 153);">a--;</span> (аналогично <span style="color: rgb(0, 0, 153);">a=a-1;</span>)</li>
            </ol>
        </p>
        <p>
            <span style="font-weight: bold;">Операции отношения:</span><br>
            <ol>
                <li>больше или равно <span style="color: rgb(0, 0, 153);">&gt;=</span></li>
                <li>больше <span style="color: rgb(0, 0, 153);">&gt;</span></li>
                <li>меньше или равно <span style="color: rgb(0, 0, 153);">&lt;=</span></li>
                <li>меньше <span style="color: rgb(0, 0, 153);">&lt;</span></li>
                <li>проверка на равенство <span style="color: rgb(0, 0, 153);">==</span></li>
                <li> проверка на неравенство <span style="color: rgb(0, 0, 153);">!=</span></li>
            </ol>
        </p>
        <p>
            <span style="font-weight: bold;">Логические операции:</span>
            <ol>
                <li>логическое и <span style="color: rgb(0, 0, 153);">&&</span></li>
                <li>логическое или<span style="color: rgb(0, 0, 153);"> ||</span></li>
                <li>отрицание<span style="color: rgb(0, 0, 153);"> !</span></li>
            </ol>
        </p>
        <p>
            <span style="font-weight: bold;">Битовые операции:</span>
            <ol>
                <li>битовое и <span style="color: rgb(0, 0, 153);">&</span></li>
                <li>битовое или <span style="color: rgb(0, 0, 153);">|</span></li>
                <li>битовое исключающее или <span style="color: rgb(0, 0, 153);">^</span></li>
                <li>сдвиг влево <span style="color: rgb(0, 0, 153);">&lt;&lt;</span></li>
                <li>сдвиг вправо <span style="color: rgb(0, 0, 153);">&gt;&gt;</span></li>
            </ol>
        </p>
        <p>
            <span style="font-weight: bold;">Математические операции:</span>
            <ol>
                <li>синус <span style="color: rgb(0, 0, 153);">sin(x)</span>.  Параметр x – переменная типа float (задаёт угол в радианах). Результат операции - переменная типа float.</li>
                <li>косинус <span style="color: rgb(0, 0, 153);">cos(x)</span>. Параметр x – переменная типа float (задаёт угол в радианах). Результат операции - переменная типа float.</li>
                <li>возведение в степень <span style="color: rgb(0, 0, 153);">pow(x,y)</span>. Возвращает переменную типа float – результат возведения переменной x в степень у.</li>
                <li>квадратный корень <span style="color: rgb(0, 0, 153);">sqrt(x)</span>. Параметр x и результат операции — переменные типа float.</li>
            </ol>
        </p>
        <p>
            <span style="font-weight: bold;">Операторы выбора:</span>
            <ol style="color: rgb(0, 0, 153);"><li>if(условие) {набор команд 1} else {набор команд 2}</li></ol>
            Если условие истинно выполняется первый набор команд, иначе — второй.<br>    if(a>=b) {c=a;} else {c=b;}<br>
            Переменной c присваивается значение наибольшей переменной из переменных a,b.<br>
            <ol style="color: rgb(0, 0, 153);" start="2"><li>(выражение 1)?(выражение 2):(выражение 3)</li></ol>
            Если выражение 1 истинно результат равен выражению 2. Иначе результат равен выражению 3.<br>
            <ol style="color: rgb(0, 0, 153);" start="3"><li>c=(a &lt; b)?a:b;</li></ol>
            Переменной c присваивается значение наименьшей переменной из переменных a,b.<br>
        </p>
        <p>
            <span style="color: rgb(204, 0, 0); font-style: italic;">Не допускается в качестве оператора выбора использовать оператор switch если внутри него используются переходы по ситуациям.</span>
        </p>
        <p><span style="font-weight: bold;"><a name="a4"></a>ОРГАНИЗАЦИЯ ЦИКЛОВ</span></p>
        <p>
            Для организации циклических процессов не рекомендуется использовать такие операторы языка Си как FOR, WHILE, DO-WHILE. Их неаккуратное применение (если время обработки цикла превышает период ситуации) может вызвать разрушение режима реального времени операционной системы и как следствие - к перезапуску контроллера.
            <br>При необходимости можно использовать цикличность выполнения ситуаций.
        </p>
        <p><span style="font-weight: bold;"><a name="a5"></a>ОБЪЯВЛЕНИЕ ПРОЦЕССОВ</span></p>
        <p>
            Процесс — обособленная часть программы, отвечающая за какие-либо действия. Активные процессы выполняются параллельно. Количество процессов произвольное. Но следует учитывать, что с ростом числа процессов увеличивается нагрузка на контроллер и кроме того, на организацию каждого процесса расходуется 8 байт оперативной памяти. Объявляется процесс в теле программы строкой <span style="color: rgb(153, 0, 0);">#PROCESS n</span>, где n – номер процесса. Запрещается создавать процессы с одинаковыми номерами. Запуск процесса осуществляется командой <span style="color: rgb(153, 0, 0);">#STARTp&lt;номер процесса&gt;;</span>. Хотя бы один процесс должен быть запущен при старте программы (в блоке инициализации <span style="color: rgb(153, 0, 0);">#INIT</span>). Процесс может быть остановлен командой <span style="color: rgb(153, 0, 0);">#STOPp&lt;номер процесса&gt;</span>. При старте процесса управление передаётся первой ситуации.
        </p>
        <p><span style="font-weight: bold;"><a name="a6"></a>ОБЪЯВЛЕНИЕ СИТУАЦИЙ</span></p>
        <p>
            Ситуация — составная часть процесса. Весь процесс представляет из себя набор ситуаций. В любой момент времени в каждом процессе активна только одна ситуация. Ситуации объявляются следующим образом: <span style="color: rgb(153, 0, 0);">#SIT&lt;номер ситуации&gt;(&lt;период ситуации&gt;)</span><br>номер ситуации — целое число<br>период ситуации — время в секундах, определяющее периодичность вызова ситуации к исполнению. Период ситуации может принимать любое значение из ряда [<span style="font-weight: bold;">0.001, 0.005, 0.01, 0.1</span>] (1, 5, 10 и 100 мс).<br>    По завершении работы операции возможен возврат на её начало (оператор #/R;) или переход на другую ситуацию (оператор <span style="color: rgb(153, 0, 0);">#/s&lt;номер ситуации&gt;;</span>). В обоих случаях переход произойдёт только через время, указанное в периоде ситуации. Для организации задержки более чем на 100 мс можно воспользоваться оператором перехода с задержкой (<span style="color: rgb(153, 0, 0);">#/&lt;t&gt;/R;</span> или <span style="color: rgb(153, 0, 0);">#/&lt;t&gt;/s&lt;номер ситуации&gt;;</span>), где t – время задержки в секундах в интервале [0...4000000]. В этом случае в расчёт берётся именно время задержки, а не период ситуации.
        </p>
        <p><span style="font-weight: bold;"><a name="a7"></a>РАБОТА С ПУЛЬТАМИ</span></p>
        <p>
            Контроллеры допускают подключение пультов в формате 4 строки по 20 символов. В среде программирования с помощью специальной утилиты заранее формируются тексты строковых сообщений, задаётся расположение и тип переменных, отображаемых на дисплее. Программа управляет лишь номером активной в данный момент строки. Для работы со строками предусмотрены системные переменные <span style="color: rgb(0, 0, 153);">st1</span>, <span style="color: rgb(0, 0, 153);">st2</span>, <span style="color: rgb(0, 0, 153);">st3</span>, <span style="color: rgb(0, 0, 153);">st4</span>. Они соответственно определяют номера активной первой, второй, третьей и четвёртой строк. На уровне операционной системы заложена прокрутка четвёртой строки кнопками «вправо» и «влево» (в случае если её номер не заблокирован программой). Это позволяет размещать в ней настроечные параметры для программы автоматики. Номер нажатой на пульте кнопки записывается в переменную key. Каждой кнопке соответствует один бит переменной. Младший бит (<span style="color: rgb(0, 0, 153);">key</span>=1) соответствует кнопке «<span style="font-weight: bold;">F1</span>», старший (<span style="color: rgb(0, 0, 153);">key</span>=128) — кнопке «<span style="font-weight: bold;">F2</span>».
        </p>
        <p>
            Возможно и побитовое обращение: <span style="color: rgb(0, 0, 153);">key_1, key_2, …, key_8</span>. <span style="color: rgb(0, 0, 153);">Key_1</span> соответствует младшему биту переменной key (<span style="font-weight: bold;">F1</span>), <span style="color: rgb(0, 0, 153);">key_8</span> – старшему (<span style="font-weight: bold;">F2</span>).
        </p>
        <p>
            <table style="text-align: left; width: 674px; height: 88px;" border="1" cellpadding="2" cellspacing="2">
                <tbody>
                    <tr>
                        <td>Номер бита</td>
                        <td>7</td>
                        <td>6</td>
                        <td>5</td>
                        <td>4</td>
                        <td>3</td>
                        <td>2</td>
                        <td>1</td>
                        <td>0</td>
                    </tr>
                    <tr>
                        <td>Значение переменной key</td>
                        <td>128</td>
                        <td>64</td>
                        <td>32</td>
                        <td>16</td>
                        <td>8</td>
                        <td>4</td>
                        <td>2</td>
                        <td>1</td>
                    </tr>
                    <tr>
                        <td>Название кнопки</td>
                        <td>F2</td>
                        <td>SHIFT</td>
                        <td>вниз</td>
                        <td>ввод</td>
                        <td>вправо</td>
                        <td>вверх</td>
                        <td>влево</td>
                        <td>F1</td>
                    </tr>
                </tbody>
            </table>
        </p>
        <p>
            Кнопка «<span style="font-weight: bold;">F2</span>», нажатая в момент подачи питания на контроллер переводит его в принудительный режим программирования. Если в течение 30 секунд процесс программирования не старотовал контроллер автоматически перезапускается.
        </p>
        <p>
            Для доступа к светодиодам пульта служат переменные led (одновременное управление всеми 8 светодиодами), <span style="color: rgb(0, 0, 153);">led_1</span>, <span style="color: rgb(0, 0, 153);">led_2</span>, <span style="color: rgb(0, 0, 153);">led_3</span>, ..., <span style="color: rgb(0, 0, 153);">led_8</span> (для управления отдельными светодиодами). В режиме редактирования переменных нажатия кнопок не записываются в переменную <span style="color: rgb(0, 0, 153);">key</span>. До тех пор пока редактирование не будет завершено она будет равна нулю. Для редактирования не доступны переменные с плавающей запятой (float).
        </p>
        <p>
            Вход в режим редактирования переменной выполняется одновременным нажатием клавиш «<span style="font-weight: bold;">shift</span>» и «<span style="font-weight: bold;">ввод</span>». Если при этом на дисплее имеются переменные для которых не установлен флаг «только чтение», напротив младшего разряда первой обнаруженной переменной появится мигающий курсор. Перемещение курсора по разрядам числа и между переменными выполняется кнопками «<span style="font-weight: bold;">влево</span>» и «<span style="font-weight: bold;">вправо</span>». Кнопки «<span style="font-weight: bold;">вверх</span>» и «<span style="font-weight: bold;">вниз</span>» соответственно увеличивают и уменьшают значение редактируемой переменной. Кнопка «ввод» подтверждает изменения и завершает процесс редактирования. Выход из режима без сохранения - повторное нажатие кнопок «<span style="font-weight: bold;">shift</span>» и «<span style="font-weight: bold;">ввод</span>».
        </p>
        <p><span style="font-weight: bold;"><a name="a8"></a>ДИСКРЕТНЫЕ И АНАЛОГОВЫЕ ВХОДЫ/ВЫХОДЫ</span></p>
        <p>
            На уровне операционной системы контроллеры поддерживают 32 базовых дискретных входа, 32 базовых дискретных выхода, 16 дополнительных дискретных входов, 16 дополнительных дискретных выходов. Дискретные входы/выходы разбиты на группы по 8 штук(один байт соответствует восьми входам/выходам), каждой из которых соответствует системная переменная:
            <ul><li>базовые дискретные входы : <span style="color: rgb(153, 51, 0);">IN0, IN1, IN2, IN3</span></li><li>базовые дискретные выходы: <span style="color: rgb(153, 51, 0);">OUT0, OUT1, OUT2 , OUT3</span></li><li>дополнительные дискретные входы: <span style="color: rgb(153, 51, 0);">DIN4, DIN5</span></li><li>дополнительные дискретные выходы <span style="color: rgb(153, 51, 0);">DOUT4, DOUT5</span>.</li></ul>
        </p>
        <p>Первый вход/выход группы соответствует младшему биту в переменной.</p>
        <p>
            Для битовых операций с входами/выходами в языке «Релкон» предусмотрен специальный формат данных:
            <ul>
                <li style="color: rgb(153, 51, 0);">#IN&lt;номер байта&gt;.&lt;номер бита&gt;</li>
                <li style="color: rgb(153, 51, 0);">#DIN&lt;номер байта&gt;.&lt;номер бита&gt;</li>
                <li style="color: rgb(153, 51, 0);">#OUT&lt;номер байта&gt;.&lt;номер бита&gt;</li>
                <li style="color: rgb(153, 51, 0);">#DOUT&lt;номер байта&gt;.&lt;номер бита&gt;</li>
                <li><span style="color: rgb(153, 51, 0);">#IN0.0</span> – соответствует первому биту первого байта дискретных входов,</li>
                <li><span style="color: rgb(153, 51, 0);">#OUT3.5 </span>– шестому биту четвёртого байта дискретных выходов и т.п.</li>
            </ul>
        </p>
        <p>
            Для работы с аналоговыми входами используются системные переменные:<br>
            <span style="color: rgb(153, 51, 0);">ADC1, ADC2, ADC3, ADC4, ADC5, ADC6, ADC7, ADC8</span><br>
            <span style="color: rgb(153, 51, 0);">    ADH1, ADH2, ADH3, ADH4, ADH5, ADH6, ADH7, ADH8</span>
        </p>
        <p>
            Переменные ADC содержат в себе 12-разрядное цифровое значение, сдвинутое на 4 бита в сторону старших разрядов. Они могу принимать значение от 0 (соответствует току 0 мА) до 65535 (20 мА). Возможное использование системных переменных AI1,...,AI8. В отличие от переменных ADC данные сдвинуты в сторону младших разрядов (диапазон от 0 до 4095). Переменные ADH применяются в случае когда достаточно точности 8 разрядов. Они могут принимать значения от 0 (0 мА) до 255 (20 мА).
        </p>
        <p>
            Для работы с аналоговыми выходами используются системные переменные: <span style="color: rgb(153, 51, 0);">DAC1, DAC2</span>. Также как и ADC они являются 12 разрядными, сдвинутыми в сторону старших разрядов. Точность преобразования гарантируется только в диапазоне 4...20 мА (значение DAC, равное 0 соответствует 0 мА, 65535 — 20 мА).
        </p>
        <p>
            Следует помнить, что если в контроллере включен режим эмуляции аппаратное состояние входов контроллера будет игнорироваться программой. Это утверждение относится и к выходам контроллера (в режиме полной эмуляции).
        </p>
        <p><span style="font-weight: bold;"><a name="a9"></a>МОДУЛИ ВВОДА/ВЫВОДА СЕРИИ MATCHBOX</span></p>
        <p>
            Контроллеры допускают подключение расширения входов/выходов в виде модулей ввода/вывода серии Matchbox по интерфейсу <span style="font-weight: bold;">RS-485</span>. Каждому модулю дискретного ввода/вывода в зависимости от его адреса соответствует системная переменная:<br>
            <span style="color: rgb(153, 51, 0);">IN4, IN5, … , IN35</span> – для модулей дискретного ввода;
            <br><span style="color: rgb(153, 51, 0);">OUT4, OUT5, … , OUT35</span> – для модулей дискретного вывода.
        </p>
        <p>
            Значащими в этих переменных являются 4 младшие бита (модули содержат не более 4 дискретных входов/выходов). Допускается работа не только с байтами, но и с отдельными битами модулей: <span style="color: rgb(153, 51, 0);">#IN4.1</span> – второй вход модуля <span style="color: rgb(153, 51, 0);">IN4</span>, <span style="color: rgb(153, 51, 0);">#OUT10.3</span> – четвёртый вход модуля <span style="color: rgb(153, 51, 0);">OUT10</span>.
        </p>
        <p>
            Модули аналогового ввода серии Matchbox могут включать в себя до 4-х аналоговых входов. В этом случае каждому модулю соответствует уже 4 системных переменных:<br><br><span style="color: rgb(153, 51, 0);">ADC9, ADC10, ADC11, ADC12</span> – для модуля ADC9...12;<br><span style="color: rgb(153, 51, 0);">ADC13, ADC14, ADC15, ADC16</span> – для модуля ADC13...16;<br>… <br><span style="color: rgb(153, 51, 0);">ADC133, ADC134, ADC135, ADC136</span> – для модуля ADC133...136.
        </p>
        <p>
            Каждый модуль аналогового вывода содержит 2 аналоговых выхода, которые связаны с двухбайтовыми переменными:<br><span style="color: rgb(153, 51, 0);">DAC5, DAC6</span> – модуль DAC5_6;<br><span style="color: rgb(153, 51, 0);">DAC7, DAC8</span> – модуль DAC7_8;<br>… <br><span style="color: rgb(153, 51, 0);">DAC67, DAC68</span> – модуль DAC67_68.
        </p>
        <p>
            Опрос модулей происходит по интерфейсу <span style="font-weight: bold;">RS-485</span> внутренним протоколом <span style="font-weight: bold;">RKFAST</span>. Следует помнить, что контроллер опрашивает лишь те модули, которые присутствуют в коде программы. Поэтому время опроса модулей может меняться в зависимости от программы. Для контроля исправности модулей имеется системная переменная Sum_err. При неправильном ответе любого модуля или при отсутствии ответа, её значение увеличивается на единицу. Эта переменная показывает общее состояние сети. Sum_err может принимать значения от 0 до 65535. При переполнении отсчёт начинается с нуля. Помимо этого существует аналогичный счётчик ошибок для каждого модуля:<br><br>E&lt;название модуля&gt;<br><br style="color: rgb(153, 51, 0);"><span style="color: rgb(153, 51, 0);">EIN4</span> – переменная контроля ошибок модуля IN4;<br><span style="color: rgb(153, 51, 0);">EADC9</span> – модуля ADC9...13;<br><span style="color: rgb(153, 51, 0);">EADC11</span> – модуля ADC9...13;<br><span style="color: rgb(153, 51, 0);">EOUT17</span> – модуля OUT17;<br><span style="color: rgb(153, 51, 0);">EDAC6</span> – модуля DAC5_6.<br><br>    Эти переменные также будут увеличиваться на единицу при обнаружении ошибки опроса модуля. Но при каждом корректном ответе счётчик будет сбрасываться в ноль. В случае отсутствия связи с модулем счётчик ошибок доходит до 100 и останавливается.
        </p>
        <p>
            Если необходимо программно отключить опрос модуля (например, в случае, если программа, в зависимости от настроек, работает с различным числом модулей), переменной контроля ошибок соответствующего модуля необходимо присвоить значение, превышающее 100.
        </p>
        <p>
            Операционная система содержит встроенные средства диагностики модулей ввода/вывода серии Matchbox. Диагностическая информация может отображаться на дисплее в сервисном меню. Вызов сервисного меню выполняется нажатием комбинации кнопок «<span style="font-weight: bold;">shift</span>» и «<span style="font-weight: bold;">F2</span>». В верхней строке выводится информация о текущей и максимальной загрузке процессора (в процентах) и общий счётчик ошибок <span style="font-weight: bold;">Sum_err</span>. В остальных строках выводятся данные по конкретным модулям (название модуля: счётчик ошибок). Напротив названия модуля отображается символ «<span style="font-weight: bold;">+</span>» или «<span style="font-weight: bold;">-</span>», в зависимости от того опрашивается модуль контроллером или нет. Прокрутка строк выполняется кнопками «вверх» и «вниз». Кнопки «влево» и «вправо» служат для ускоренного перемещения между группами модулей (<span style="color: rgb(153, 51, 0);">IN, OUT, ADC, DAC</span>). Кнопка «<span style="font-weight: bold;">F1</span>» сбрасывает общий счётчик ошибок в ноль. Кнопка «ввод» отображает данные о версии операционной системы, работающей в контроллере. Возврат в обычный режим осуществляется повторным нажатием «<span style="font-weight: bold;">shift</span>» и «<span style="font-weight: bold;">F2</span>».
        </p>
        <p><span style="font-weight: bold;"><a name="a10"></a>СИСТЕМНОЕ ВРЕМЯ КОНТРОЛЛЕРА</span></p>
        <p>
            Для работы с временными параметрами предусмотрены системные структуры times и wr_times. В их состав входят следующие переменные:<br><br>
            <span style="color: rgb(153, 51, 0);">times.sec / wr_times.sec</span> – секунды<br>
            <span style="color: rgb(153, 51, 0);">times.min / wr_times.min</span> – минуты<br>
            <span style="color: rgb(153, 51, 0);">times.hour / wr_times.hour</span> – часы<br>
            <span style="color: rgb(153, 51, 0);">times.date / wr_times.date</span> – число<br>
            <span style="color: rgb(153, 51, 0);">times.month / wr_times.month</span> – месяц<br>
            <span style="color: rgb(153, 51, 0);">times.year/ wr_times.year</span> - год
        </p>
        <p>
            Переменные структуры times доступны для вывода на пульт. Если разрешено их редактирование, то изменение этих переменных с пульта приведёт к изменению системного времени. Для чтения текущего времени из программы достаточно обратиться к этим переменным.<br>    Структура <span style="color: rgb(153, 51, 0);">wr_times</span> служит для корректировки времени из программы. В неё записываются необходимые временные параметры, а затем вызывается функция <span style="color: rgb(153, 51, 0);">set_time()</span>. Для правильной установки времени необходимо присвоить значения всем полям структуры <span style="color: rgb(153, 51, 0);">wr_times</span>.<br>    В приведённых выше структурах отсутствуют параметры, задающие год и день недели. Не учитываются високосные года и переход на летнее и зимнее время.
        </p>
        <p><span style="font-weight: bold;"><a name="a11"></a>РАБОТА С FRAM</span></p>
        <p>
            <span style="font-weight: bold;">FRAM</span> - энергонезависимая память, сохраняющая своё состояние и после пропадания питания. Объём памяти доступной в контроллере — 32 кБайт. Часть этого пространства используется для хранения заводских установок и системных настроек, т.е. зарезервирована операционной системой контроллера. Эта область располагается в конце памяти по адресам 0x7B00...0x7FFF. В распоряжении пользователя находится память в диапазоне <span style="font-weight: bold;">0x0000...0x7AFF1</span>. Это более 30 кБайт данных.
        </p>
        <p>
            Чтение данных выполняется функцией <span style="color: rgb(51, 102, 102);">read_fram</span>(<span style="font-style: italic;">Adr,Size,Ptr</span>), <br>    
            где <span style="font-style: italic;">Adr</span> – начальный адрес (переменная типа unsigned short);<br>    
            <span style="font-style: italic;">Size</span> – количество байт для чтения (переменная типа unsigned char; должна быть в диапазоне от 1 до 128 включительно);<br>    
            <span style="font-style: italic;">Ptr </span>– указатель на массив или переменную, куда будет выполнено сохранение данных (адрес в памяти контроллера).
        </p>
        <p>Ниже приведён пример чтения из FRAM 10 байт в массив my_array, начиная с адреса 0x00 и чтение ячейки по адресу 0x80 в однобайтовую переменную my_var.</p>
        <pre>
            <span style="color: rgb(153, 51, 0);">#DATA</span>   
            <span style="color: rgb(0, 153, 0);">// Объявление переменных</span>       
            <span style="color: rgb(153, 51, 0);">#define</span> AR_SIZE 100        
            <span style="color: rgb(0, 0, 153);">   unsigned char</span> my_var;        
            <span style="color: rgb(0, 0, 153);">   unsigned short</span> my_array[AR_SIZE];       
            <span style="color: rgb(153, 51, 0);">#INIT</span>       
            <span style="color: rgb(0, 153, 0);">// Инициализация</span>      
            <span style="color: rgb(153, 51, 0);">#STARTp0;</span>       
            <span style="color: rgb(0, 153, 0);">// Начало программы</span>
            
            <span style="color: rgb(153, 51, 0);">#PROCESS 0</span>  
            
            <span style="color: rgb(153, 51, 0);">#SIT1(0.1)</span>       
            <span style="color: rgb(51, 102, 102);">    read_fram</span>(0x00,10,my_array);       
            <span style="color: rgb(51, 102, 102);">    read_fram</span>(0x80,1,&my_var);        
            <span style="color: rgb(153, 51, 0);">#/R;</span>
        </pre>
        <p>
            Для записи FRAM памяти используется функция <span style="color: rgb(51, 102, 102);">write_fram</span>(<span style="font-style: italic;">Adr,Size,Ptr</span>). Параметры этой функции аналогичны параметрам функции read_fram, за тем исключением, что Ptr указывает на переменную или массив откуда будут браться данные для записи в память. Работа с FRAM памятью выполняется медленнее, чем с оперативной. Поэтому функции <span style="color: rgb(51, 102, 102);">read_fram</span> и <span style="color: rgb(51, 102, 102);">write_fram</span> необходимо вызывать только из ситуаций, работающих в 100 мс цикле.
        </p>
        <p><span style="font-weight: bold;"><a name="a12"></a>ЗАВОДСКИЕ УСТАНОВКИ</span></p>
        <p>
            Заводские установки располагаются в системной области FRAM памяти. Они позволяют облегчить работу с настройками контроллера. Под заводские установки зарезервировано 1024 байта. При включении контроллер считывает из FRAM памяти все установки и отображает их на область оперативной памяти. Это связано с тем, что быстродействие оперативной памяти выше. Для обращения к этому слепку заводских установок используются системные макросы:<br>
            <ul>
                <li style="color: rgb(153, 102, 51);">    EE_1(x1)</li>
                <li style="color: rgb(153, 102, 51);">    EE_2(x2)</li>
                <li style="color: rgb(153, 102, 51);">    EE_4(x3),</li>
            </ul>где x – номер ячейки.
        </p>
        <p>
            EE_1 представляет память как 1024 однобайтовых переменных и соответственно x1 может принимать значения от 0 до 1023.<br>
            EE_2 разбивает область установок на 512 двухбайтовых переменных (x2 в диапазоне от 0 до 511).<br>
            EE_4 используется если необходимы четырёхбайтовые переменные (x3 в диапазоне от 0 до 255).
        </p>
        <p>
            Следует помнить, что эти области перекрываются между собой. Например EE_4(0) занимает то же пространство, что и совокупность переменных EE_1(0), EE_1(1), EE_1(2), EE_1(3). Допускается также следующий вариант обращения к заводским установкам:<br><br>
            <span style="color: rgb(153, 102, 51);">EE0, EE1, EE2, EE3, EE4</span>, … - для однобайтовых;<br>
            <span style="color: rgb(153, 102, 51);">EE0i, EE2i, EE4i, EE6i, EE8i</span>, … - для двухбайтовых;<br>
            <span style="color: rgb(153, 102, 51);">EE0l, EE4l, EE8, EE12l, EE16l</span>, … - для четырёхбайтовых.
        </p>
        <p>
            При редактировании заводских установок с пульта меняется содержимое как оперативной памяти, так и соответствующей ей области FRAM памяти. Однако, если в программе присвоить переменной EE какое-либо значение, то это приведёт лишь к изменению оперативной памяти, не повлияв на FRAM. В этом случае необходимо воспользоваться функцией write_fram. Заводские установки хранятся в FRAM в диапазоне <span style="font-weight: bold;">0x7B00...0x7EFF</span>, где 0x7B00 связан с переменной EE_1(0)1, а 0x7EFF — с переменной EE_1(1023).
        </p>
        <p><span style="font-weight: bold;"><a name="a13"></a>ПАМЯТЬ ПОЛЬЗОВАТЕЛЯ</span></p>
        <p>
            Память пользователя ограничена размером в 256 байт. Для обращения к ней используются системные перменные <span style="color: rgb(153, 51, 0);">mem0,mem1,mem2,mem3,...,mem255</span> (однобайтные беззнаковые), <span style="color: rgb(153, 51, 0);">memoi, mem2i, mem4i, mem6i, ... , mem254i</span> (двухбайтовые беззнаковые),<span style="color: rgb(153, 51, 0);"> mem0l, mem4l, mem8l, mem12l, ... , mem252l</span> (четырёхбайтовые знаковые), и <span style="color: rgb(153, 51, 0);">mem0fl, mem4fl, mem8fl, mem12fl, ... , mem252fl</span> (четырёхбайтовые с плавающей запятой). Основное назначение памяти пользователя — облегчение доступа к данным контроллера для систем диспетчеризации. Возможно обращение к данной памяти по интерфейсам RS485 и/или Ethernet по протоколу Modbus (RTU/ASCII/TCP) как к HOLDING регистрам (см. описание протокола Modbus) в диапазоне адресов [0...127]. 
        </p>
        <p><span style="font-weight: bold;"><a name="a13_2"></a>РАСШИРЕННАЯ ПАМЯТЬ ПОЛЬЗОВАТЕЛЯ</span></p>
        <p>
            Расширенная память пользователя (<span style="color: rgb(153, 51, 0);">emem</span>) представляет собой массив двухбайтных беззнаковых переменных размерность которого (от 10 до 1000) задаётся в настройках проекта (в среде разработке). Возможно обращение к данной памяти по интерфейсам RS485 и/или Ethernet по протоколу Modbus (RTU/ASCII/TCP) как к HOLDING регистрам (см. описание протокола Modbus) начиная со 128 адреса (переменная <span style="color: rgb(153, 51, 0);">emem[0]</span> соответствует holding регистру с адресом 128). Верхняя граница диапазона зависит от размерности массива. 
        </p>
        <p><span style="font-weight: bold;"><a name="a14"></a>КОНТРОЛЬ ЗАГРУЗКИ ПРОЦЕССОРА</span></p>
        <p>
            Системные переменные <span style="color: rgb(153, 51, 0);">_SysRealTmr</span> и <span style="color: rgb(153, 51, 0);">maxload</span> позволяют контролировать текущую и максимальную (с момента подачи питания) степень загруженности контроллера. Они высчитываются операционной системой и могут принимать значения от 0 до 1000, где 1000 соответствует стопроцентной загрузке. Эти же данные (только в процентах) выводятся в диагностическом меню на пульте (вход/выход выполняется нажатием комбинации кнопок «<span style="font-weight: bold;">shift</span>» и «<span style="font-weight: bold;">F2</span>»).
        </p>
        <p><span style="font-weight: bold;"><a name="a15"></a>ЗАРЕЗЕРВИРОВАННЫЕ СЛОВА</span></p>
        <p>
            Ниже в таблице приведён перечень зарезервированных слов. Не допускается использовать их в тексте программы для объявления переменных.<br><br>
            <table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2">
                <tbody>
                    <tr>
                        <td>_Sys</td>
                        <td> adc_sum</td>
                        <td> OUT</td>
                        <td> st2</td>
                    </tr>
                    <tr>
                        <td>_Sys_ADC</td>
                        <td> c1_n</td>
                        <td> r1</td>
                        <td> st3</td>
                    </tr>
                    <tr>
                        <td>_Sys_DAC</td>
                        <td> err_mod</td>
                        <td> r5</td>
                        <td> st4</td>
                    </tr>
                    <tr>
                        <td>_Sys_IN</td>
                        <td>IN</td>
                        <td> r10</td>
                        <td> Sum_err</td>
                    </tr>
                    <tr>
                        <td> _Sys_OUT</td>
                        <td> IO_DEF</td>
                        <td> r100</td>
                        <td> times</td>
                    </tr>
                    <tr>
                        <td>_SysKey</td>
                        <td> key</td>
                        <td> sd_addr</td>
                        <td> wr_times</td>
                    </tr>
                    <tr>
                        <td>_SysRealTmr</td>
                        <td> led</td>
                        <td> sd_buf</td>
                        <td> Z40</td>
                    </tr>
                    <tr>
                        <td>_ADC</td>
                        <td> max_load</td>
                        <td> sd_stat</td>
                        <td> Z50</td>
                    </tr>
                    <tr>
                        <td>_DAC</td>
                        <td>month_sec</td>
                        <td>st1</td>
                        <td></td>
                    </tr>
                </tbody>
            </table>
        </p>
        <p><span style="font-weight: bold;"><a name="a16"></a>ПЕРЕСЧЁТ ДАННЫХ АЦП</span></p>
        <p>
             Если аналоговый датчик реализует линейную зависимость тока от измеряемого параметра, то её можно описать функцией y=k*x+b, где x-данные АЦП, y – непосредственно измеряемый параметр (температура, давление, …), k и b – постоянные величины. Зная две любые точки зависимости (например значение температуры при 4 мА и при 20 мА) можно вычислить константы k и b.
        </p>
        <p>
            <span style="font-weight: bold;">        <span style="font-style: italic;">y1=k*x1+b</span></span><br>
            <span style="font-weight: bold; font-style: italic;">        y2=k*x2+b</span><br>
            <span style="font-weight: bold; font-style: italic;">        k=(y2-y1)/(x2-x1)</span><br>
            <span style="font-weight: bold; font-style: italic;">        b=y1-k*x1</span>
        </p>
        <p>
            Току 4 мА соответствует значение переменной (ADC>>4)1 – 819; 20 мА — 4095.<br>    Для датчика температуры 0...200 ° С (4...20 мА) :
        </p>
        <p>
            <span style="font-style: italic; font-weight: bold;">k=(200-0)/(4095-819)=200/3276=50/819.</span><br>
            <span style="font-style: italic; font-weight: bold;">b=0-(50/819)*819=-50.</span><br>
            <span style="font-style: italic; font-weight: bold;">T=(50*(ADC>>4))/819-50.</span>
        </p>
        <p>
            Вместо ADC>>4 можно допускается использовать переменную AI. Перед умножением переменную ADC рекомендуется принудительно преобразовать к типу unsigned long или float, чтобы в результате умножения не возникло переполнения. Если необходимо учитывать знаки после запятой, результирующую переменную (в данном примере T) нужно объявить как float.
        </p>
        <p>
            T=(50*(<span style="color: rgb(0, 0, 153);">unsigned long</span>)(<span style="color: rgb(153, 51, 0);">ADC1</span>>>4))/819-50; или T=(50*(<span style="color: rgb(0, 0, 153);">unsigned long</span>)<span style="color: rgb(153, 51, 0);">AI1</span>)/819-50;
        </p>
        <p>
            Аналогичным образом можно получить формулу для датчика температуры -50...50 ° С .<br><br>    T=(25*(<span style="color: rgb(0, 0, 153);">unsigned long</span>)(<span style="color: rgb(153, 51, 0);">ADC1</span>>>4))/819-75; или T=(25*(<span style="color: rgb(0, 0, 153);">unsigned long</span>)<span style="color: rgb(153, 51, 0);">AI1</span>)/819-75;
        </p>
        <p><span style="font-weight: bold;"><a name="a17"></a>РАБОТА С SD КАРТОЙ ПАМЯТИ</span></p>
        <p>
            Для управления картой памяти служит системная переменная <span style="color: rgb(153, 51, 0);">sd_stat</span>. При запуске контроллера её значение равно нулю. Независимо от того используется карта памяти или нет выполняется попытка её инициализации. Если SD карта обнаружена и успешно инициализирована переменная <span style="color: rgb(153, 51, 0);">sd_stat</span> примет значение, равное 5 (значению 5 соответствует системный макрос <span style="color: rgb(153, 51, 0);">INIT_COMPL</span>). Запись и чтение в карту памяти выполняется только постранично. Размер страницы составляет 512 байт. Адрес страницы указывается в переменной<span style="color: rgb(153, 51, 0);"> sd_addr</span>. Его максимальное значение зависит от размера карты (1 килобайт включает две страницы). Результат чтения содержится в буфере <span style="color: rgb(153, 51, 0);">sd_buf</span> (512 переменных типа unsigned char). В него же подставляются необходимые данные в случае записи. Для чтения карты переменной <span style="color: rgb(153, 51, 0);">sd_stat</span> необходимо присвоить 2 (или системный макрос <span style="color: rgb(153, 51, 0);">CMD_RD</span>). Если чтение будет выполнено без ошибок <span style="color: rgb(153, 51, 0);">sd_stat</span> примет значение 6 (макрос <span style="color: rgb(153, 51, 0);">RD_COMPL</span>). Для записи данных в карту переменной sd_stat необходимо присвоить 7 (макрос <span style="color: rgb(153, 51, 0);">CMD_WR</span>). После успешной записи <span style="color: rgb(153, 51, 0);">sd_stat</span> станет равна 9 (макрос <span style="color: rgb(153, 51, 0);">WR_COMPL</span>). В случае ошибок при чтении или записи автоматически предпринимается до 5 повторных попыток. Если они все оказались безуспешными выполняется переинициализация SD карты. Ниже приведён пример чтения/записи SD карты памяти.
        </p>
        <pre>
            <span style="color: rgb(153, 51, 0);">#DATA</span>
            <span style="color: rgb(0, 102, 0);">// Объявление переменных</span>
            <span style="color: rgb(0, 0, 153);">   unsigned char</span> wr_var1,wr_var2;
            <span style="color: rgb(0, 0, 153);">   unsigned short</span> wr_var3;
            <span style="color: rgb(0, 0, 153);">   unsigned char </span>rd_var1,rd_var2;
            <span style="color: rgb(0, 0, 153);">   unsigned short </span>rd_var3;
            <span style="color: rgb(153, 51, 0);">#INIT</span>
            <span style="color: rgb(0, 102, 0);">// Инициализация</span>
            <span style="color: rgb(153, 51, 0);">#STARTp0;</span> 
            wr_var1=100;wr_var2=200;wr_var3=10000;<span style="color: rgb(0, 102, 0);">
            // Начало программы</span>
            <span style="color: rgb(153, 51, 0);">#PROCESS 0</span>
            
            <span style="color: rgb(153, 51, 0);">#SIT1(0.1)</span>
            <span style="color: rgb(0, 0, 153);">   if</span>(<span style="color: rgb(102, 0, 0);">sd_stat</span>==<span style="color: rgb(102, 0, 0);">INIT_COMPL</span>) <span style="color: rgb(153, 51, 0);">#/s2;</span>
            <span style="color: rgb(153, 51, 0);">#/R;</span>
            
            <span style="color: rgb(153, 51, 0);">#SIT2(0.1)</span>
            <span style="color: rgb(102, 0, 0);">   sd_addr</span> = 0;
            <span style="color: rgb(102, 0, 0);">   sd_buf</span>[0]=wr_var1;
            <span style="color: rgb(102, 0, 0);">   sd_buf</span>[1]=wr_var2;
            <span style="color: rgb(102, 0, 0);">   sd_buf</span>[2]=wr_var3&gt;&gt;8;
            <span style="color: rgb(102, 0, 0);">   sd_buf</span>[3]=wr_var3&0xFF;
            <span style="color: rgb(102, 0, 0);">   sd_stat</span>=<span style="color: rgb(102, 0, 0);">CMD_WR</span>;
            <span style="color: rgb(153, 51, 0);">#/s3;</span>
            
            <span style="color: rgb(153, 51, 0);">#SIT3(0.1)</span>
            <span style="color: rgb(0, 0, 153);">   if</span>(<span style="color: rgb(102, 0, 0);">sd_stat</span>!=<span style="color: rgb(102, 0, 0);">WR_COMPL</span>) <span style="color: rgb(153, 51, 0);">#/s6;</span>
            <span style="color: rgb(102, 0, 0);">   sd_addr</span>=0; 
            <span style="color: rgb(102, 0, 0);">   sd_buf</span>[0]=<span style="color: rgb(102, 0, 0);">sd_buf</span>[1]=<span style="color: rgb(102, 0, 0);">sd_buf</span>[2]=<span style="color: rgb(102, 0, 0);">sd_buf</span>[3]=0;
            <span style="color: rgb(102, 0, 0);">   sd_stat</span>=<span style="color: rgb(102, 0, 0);">CMD_RD</span>;
            <span style="color: rgb(153, 51, 0);">#/s4;</span>
            
            <span style="color: rgb(153, 51, 0);">#SIT4(0.1)</span>
            <span style="color: rgb(0, 0, 153);">   if</span>(<span style="color: rgb(102, 0, 0);">sd_stat</span>!=<span style="color: rgb(102, 0, 0);">RD_COMP</span>L) <span style="color: rgb(153, 51, 0);">#/s6;</span>     rd_var1=<span style="color: rgb(102, 0, 0);">sd_buf</span>[0];rd_var2=<span style="color: rgb(102, 0, 0);">sd_buf</span>[1];
                rd_var3=(((unsigned short)<span style="color: rgb(102, 0, 0);">sd_buf</span>[2])&lt;&lt;8) | <span style="color: rgb(102, 0, 0);">sd_buf</span>[3];
            <span style="color: rgb(153, 51, 0);">#/s5;</span>
            
            <span style="color: rgb(153, 51, 0);">SIT5(0.1)</span>
            <span style="color: rgb(0, 102, 0);">// Данные успешно записаны и прочитаны</span>
            <span style="color: rgb(153, 51, 0);">#/R;</span>
            
            <span style="color: rgb(153, 51, 0);">#SIT6(0.1)</span>
            <span style="color: rgb(0, 102, 0);">// Ошибка чтения или записи</span>
            <span style="color: rgb(102, 0, 0);">   sd_stat</span>=<span style="color: rgb(102, 0, 0);">CMD_INIT</span>;
            <span style="color: rgb(153, 51, 0);">#/s1;</span>
        </pre>
        <p><span style="font-weight: bold;"><a name="a18"></a>РАБОТА С ИНТЕРФЕЙСОМ RS485</span></p>
        <p>
            Стандартные макросы, применяемые в функциях:<br>
            номер канала<br>
            <li>CAN_PR - канал программирования контроллера</li>
            <li>CAN_PC – канал подключения внешних устройств</li>
            <li>CAN_MB – канал модулей ввода/вывода</li>

            примеч.: работа с каналом расширения ввода/вывода (CAN_MB) возможна только при 
            отсутствии в программе ссылок на модульные входы/выходы (MATCHBOX).
        </p>
        <p>
            В языке <span style="font-weight: bold;">Релкон</span> доступны следующие функции для работы с каналом:
        </p>
        <p>
            <h3 style="color:darkblue">Запрет стандартного протокола канала:</h3>
            Необходим при использовании канала в режиме мастера<br>
            <span style="font-weight: bold; font-style: italic; color:blue">can_disable(unsigned char num);</span><br>
            num – номер канала (CAN_PC, CAN_PR, CAN_MB)<br>
            <small>примеч.:  запрет протокола на канале программирования приведёт к невозможности последующей перепрошивки контроллера без внешнего пульта (ПУ134 — необходим для принудительного запуска режима программирования</small><br>
            <small>Следует помнить, что через канал PROG выполняется программирование контроллера и если запретить стандартный протокол, то контроллер не сможет автоматически перейти в режим программирования по внешним запросам от среды программирования. В этом случае перейти в режим программирования необходимо вручную, используя кнопку «F2» (более подробно см. описание работы с пультами).</small>
        </p>
        <p>
            <h3 style="color:darkblue">Разрешение стандартного протокола канала:</h3>
            Канал контроллера автоматически отвечает на запросы по протоколу<br>
            <span style="font-weight: bold; font-style: italic; color:blue">can_enable(unsigned char num);</span><br>
            num – номер канала<br>
            <small>примеч.: по умолчанию стандартные протоколы (RK BIN/ASCII,Modbus RTU/ASCII) разрешены.</small>
        </p>
        <p>
            <h3 style="color:darkblue">Указатель на передающий буфер канала</h3>
            Возвращает адрес буфера передачи<br>
            <span style="font-weight: bold; font-style: italic; color:blue">unsigned char* get_can_tx_ptr(unsigned char num);</span><br>
            num – номер канала<br>
            <small>примеч.: размерность буферов для всех каналов — 512 байт</small><br>
            <pre>
    unsigned char* tx_buf;
    tx_buf = get_can_tx_ptr(CAN_PC);
    tx_buf[0]='T';
    tx_buf[1]='e';
    tx_buf[2]='s';
    tx_buf[3]='t';
            </pre>
        </p>
        <p>
            <h3 style="color:darkblue">Указатель на приёмный буфер канала</h3>
            Возвращает адрес буфера приёма<br>
            <span style="font-weight: bold; font-style: italic; color:blue">unsigned char* get_can_rx_ptr(unsigned char num);</span><br>
            num – номер канала<br>
            <small>примеч.: размерность буферов для всех каналов — 512 байт</small>
            <pre>
    unsigned char* rx_buf;
    rx_buf = get_can_rx_ptr(CAN_PC);
            </pre>            
        </p>
        <p>
            <h3 style="color:darkblue">Количество байт данных в приёмном буфере</h3>
            Возвращает количество принятых каналом байт данных<br>
            <span style="font-weight: bold; font-style: italic; color:blue">unsigned short get_rx_cnt(unsigned char num);</span><br>
            num – номер канала<br>
        </p>
        <p>
            <h3 style="color:darkblue">Сброс приёмного буфера</h3>
            Обнуляет счётчик принятых байт канала<br>
            <span style="font-weight: bold; font-style: italic; color:blue">void clear_rx_cnt(unsigned char num);</span><br>
            num – номер канала<br>
        </p>
        <p>
            <h3 style="color:darkblue">Очистка буфера</h3>
            Сбрасывает счётчик принятых байт буфера и обнуляет его содержимое<br>
            <span style="font-weight: bold; font-style: italic; color:blue">clear_buf(unsigned char num)</span> <br>
            num - номер канала
            <small>примеч.: работает только для каналов PC и PR.</small>
        </p>
        <p>
            <h3 style="color:darkblue">Отправка данных</h3>
            Посылает данные из буфера в канал<br>
            <span style="font-weight: bold; font-style: italic; color:blue">void send(unsigned char num,unsigned char* ptr, unsigned short cnt);</span><br>
            num – номер канала<br>
            ptr – указатель на внешний буфер типа unsigned char<br>
            cnt – количество байт для отправки<br>
            <pre>
    unsigned char my_tx[10];
    my_tx[0]=0x01;
    my_tx[1]=0xA0;
    my_tx[2]=0x00;
    my_tx[3]=0x58;
    send(CAN_PC,my_tx,4);
            </pre>
        </p>
        <p>
            <h3 style="color:darkblue">Отправка строки</h3>
            Посылает строку в канал<br>
            <span style="font-weight: bold; font-style: italic; color:blue">void send_str(unsigned char num,char* ptr);</span><br>
            num – номер канала<br>
            ptr – указатель на внешний буфер, содержащий строку<br>
            <pre>
    send_str(CAN_PC,"AT+CREG?\r\n");
            </pre>
        </p>
        <p>
            <h3 style="color:darkblue">Поиск строки в приёмном буфере</h3>
            Возвращает результат поиска строки в приёмном буфере(1 – найдена, 0 — не найдена)<br>
            <span style="font-weight: bold; font-style: italic; color:blue">char search(unsigned char num,unsigned char* ptr);</span><br>
            num – номер канала<br>
            ptr – указатель на строку<br>

            <small>примеч.: признаком окончания строки является нулевой символ</small><br>
            <code>if(search(CAN_PC,”OK”)) #OUT0.0=1;else #OUT0.0=0;</code>
        </p>
        <p>
            <h3 style="color:darkblue">Отправка стандартных команд</h3>
            Позволяет послать в передающий буфер некоторые команды протоколов RKBIN,RK51BIN,RK51ASCII и MODBUS RTU.<br>
            <span style="font-weight: bold; font-style: italic; color:blue">void can_cmd(request* r);</span><br>
            r – указатель на структуру типа request<br>
            <pre>
struct
{
    unsigned char canal;
    unsigned char cmd;
    unsigned char plc_addr;
    unsigned short mem_addr;
    unsigned short amount;
    unsigned char* rx;
    unsigned char* tx;
}request;
            </pre>
            canal – номер канала<br>
            cmd – код команды<br>
            plc_addr – сетевой адрес контроллера, к которому обращён запрос<br>
            mem_addr – начальный адрес памяти, к которой обращён запрос<br>
            amount – количество байт (или регистров - для протокола Modbus) для чтения/записи<br>
            rx – указатель на буфер, в котором будет сохранён ответ(присваивается автоматически операционной системой контроллера)<br>
            tx – указатель на буфер с данными для записи<br>
        </p>
        <p>
            Поддерживаемые типы команды (cmd):<br>
            <li>RD_RAM  – чтение оперативной памяти контроллеров на базе CORTEX</li>
            <li>RD_US   – чтение памяти пользователя(область для диспетчеризации) контроллеров на базе CORTEX</li> 
            <li>RD_US_ASCII   – чтение памяти пользователя(область для диспетчеризации) контроллеров на базе CORTEX (ASCII протокол)</li> 
            <li>RD_IO   - чтение стандартных входов/выходов контроллеров на базе CORTEX</li> 
            <li>RD_RAM51 - чтение оперативной памяти контроллеров на базе AT89C51ED2</li> 
            <li>RD_XRAM51 — чтение расширенной оперативной памяти контроллеров на базе AT89C51ED2</li> 
            <li>RD_RAM51_ASCII - чтение оперативной памяти контроллеров на базе AT89C51ED2 (ASCII протокол)</li> 
            <li>RD_XRAM51_ASCII — чтение расширенной оперативной памяти контроллеров на базе AT89C51ED2 (ASCII протокол)</li>             
            <li>WR_RAM  – запись оперативной памяти контроллеров на базе CORTEX</li> 
            <li>WR_US   - запись памяти пользователя(область для диспетчеризации) контроллеров на базе CORTEX</li> 
            <li>WR_XRAM — запись расширенной оперативной памяти контроллеров на базе AT89C51ED2</li> 
            <li>RD_HLD — чтение Holding регистров по протоколу Modbus RTU (Modbus код - 0x03)</li> 
            <li>RD_INP – чтение Input регистров по протоколу Modbus RTU (Modbus код - 0x04)</li> 
            <li>RD_DINP — чтение  Input Status по протоколу Modbus RTU (Modbus код - 0x02)</li> 
            <li>RD_COILS — чтение дискретных выходов по протоколу Modbus RTU (Modbus код - 0x01)</li> 
            <li>WR_HLD — запись HOLDING регистров по протоколу Modbus RTU (Modbus код - 0x10)</li> 
            <li>WR_COIL — запись дискретного выхода по протоколу Modbus RTU (Modbus код - 0x05)</li> 
        </p>
        <p>Примеры запросов:</p>
        <p><b>Чтение первых 2 байт из области стандартных входов/выходов контроллера с первым сетевым адресом  (IN0,IN1)</b></p>
        <pre>
    request req;
    req.canal = CAN_PC;
    req.cmd = RD_IO;
    req.plc_addr = 1;
    req.mem_addr=0;
    req.amount = 2;
    can_cmd(&req);
        </pre>
        <p><b>Чтение 8 байт расширенной оперативной памяти из контроллера на базе  AT89C51ED2 (сетевой адрес контроллера — 2, адрес ячейки памяти   0x600)</b></p>
        <pre>
    request req;
    req.canal = CAN_PC;
    req.cmd = RD_XRAM51;
    req.plc_addr = 2;
    req.mem_addr=0x600;
    can_cmd(&req);
        </pre>
        <p>Для данной команды количество байт фиксировано и всегда равно 8. Это обусловлено особенностью протокола RK51BIN.</p>
        <p><b>Чтение 100 байт памяти пользователя с нулевого адреса (сет. адрес контр. -  5)</b></p>
        <pre>
    request req;
    req.canal = CAN_PC;
    req.cmd = RD_US;
    req.plc_addr = 5;
    req.mem_addr=0;
    req.amount = 100;
    can_cmd(&req);
        </pre>
        <p><b>Запись трёх байт в память пользователя контроллера на базе  CORTEX</b></p>
        <pre>
    #DATA
    // Присвоение переменных программы.
        request req;
        unsigned char tx_data[3]={0x03,0x02,0x04};
    #INIT
    #STARTp0;
    //-----Начало программы.
    #PROCESS 0
    #SIT1(0.1)
        req.canal = CAN_PC;
        req.cmd = WR_US;
        req.plc_addr = 1;
        req.mem_addr=0;
        req.amount = 3;
        req.tx = tx_data;
        can_cmd(&req);
    #/1.0/R;
        </pre>
        <p>
            <h3 style="color:darkblue">Проверка ответа на стандартные команды чтения</h3>
            Возвращает результат проверки ответа на ранее сформированный запрос (1 — ответ корректный, 0 — ошибка)<br>
            <span style="font-weight: bold; font-style: italic; color:blue">char can_check(request* r);</span><br>
            r – указатель на структуру типа request<br>
            <small>Примеч.: Функция  can_check  должна вызываться с паузой после отсылки команды  can_cmd, необходимой для надёжного принятия ответа</small><br>
            Пример:<br>
            <pre>
    request req;
    unsigned char var1,var2;  // переменные, в которых сохраняется результат
    unsigned short err_cnt=0; // счётчик ошибок
    req.canal = CAN_PC;
    req.cmd = RD_XRAM51;
    req.plc_addr = 2;
    req.mem_addr=0x600;
    can_cmd(&req);
    // пауза — 0,5 секунды
    if(can_check(&req))
    {
       var1 = req.rx[0];
       var2 = req.rx[1];
       err_cnt=0;
    }else err_cnt++;

            </pre>
        </p>
        <p><b>КОМАНДЫ РАБОТЫ С МОДЕМОМ</b></p>
        <p> Работу с модемом можно организовать, используя ранее описанные функции <i>send</i>, <i>send_str</i> и <i>search</i>. Но для удобства некоторые стандартные команды оформлены в виде функций.</p>
        <p>
            <h3 style="color:darkblue">Набора номера в голосовом режиме</h3>
            Аналогична выполнению команды модема ATD в голосовом режиме<br>
            <span style="font-weight: bold; font-style: italic; color:blue">call(unsigned char can, unsigned char* num);</span><br>
            num – указатель на массив, содержащий номер телефона. Признак окончания номера нулевой символ.<br>
            <code>call(CAN_PC,”12345”);</code>
        </p>
        <p>
            <h3 style="color:darkblue">Набора номера в режиме передачи данных</h3>
            Аналогична выполнению команды модема ATD в режиме CSD<br>
            <span style="font-weight: bold; font-style: italic; color:blue">call_data(unsigned char can, unsigned char* num);</span><br>
            num – указатель на массив, содержащий номер телефона. Признак окончания номера нулевой символ.<br>
            <code>call_data(CAN_PC,”12345”);</code>
        </p>
        <p>
            <h3 style="color:darkblue">Набора номера (с автоматическим добавлением к началу "+7") в голосовом режиме</h3>
            Аналогична выполнению команды модема ATD в голосовом режиме<br>
            <span style="font-weight: bold; font-style: italic; color:blue">call7(unsigned char can, unsigned char* num);</span><br>
            num – указатель на массив, содержащий номер телефона. Признак окончания номера нулевой символ.<br>
            <code>call7(CAN_PC,”9209170680”);</code>
        </p>
        <p>
            <h3 style="color:darkblue">Набора номера (с автоматическим добавлением к началу "+7") в режиме передачи данных</h3>
            Аналогична выполнению команды модема ATD в режиме CSD<br>
            <span style="font-weight: bold; font-style: italic; color:blue">call_data7(unsigned char can, unsigned char* num);</span><br>
            num – указатель на массив, содержащий номер телефона. Признак окончания номера нулевой символ.<br>
            <code>call_data7(CAN_PC,”9209170680”);</code>
        </p>
        <p>
            <h3 style="color:darkblue">Отправка заголовка SMS</h3>
            Формирует заголовок, необходимый для отправки SMS.<br>
            <span style="font-weight: bold; font-style: italic; color:blue">write_head(unsigned char can);</span><br>
            can – номер канала<br>
        </p>
        <p>
            <h3 style="color:darkblue">Отправка тела SMS</h3>
            Формирует содержимое для отправки SMS.<br>
            <span style="font-weight: bold; font-style: italic; color:blue">send_sms(unsigned char can, unsigned char* num, unsigned char* data);</span><br>
            can – номер канала<br>
            num - указатель на буфер с номером получателя<br>
            data - указатель на буфер с содержимым сообщения<br>
            <small>Номер должен быть стандартным 11-значным, но без «8» или «+7». Функция должна вызываться спустя как минимум пол секунды после команды write_head(). Длина сообщения не должна превышать 70 символов.</small>
        </p>
        <pre>
            <span style="color: rgb(102, 51, 51);">#DATA</span>
            <span style="color: rgb(102, 51, 51);">#INIT</span>
            <span style="color: rgb(102, 51, 51);">#STARTp0;</span>
            <span style="color: rgb(0, 102, 0);">//-----Начало программы.</span>  
            <span style="color: rgb(102, 51, 51);">#PROCESS 0</span>
            <span style="color: rgb(102, 51, 51);">#SIT1(0.1)</span>
            <span style="color: rgb(51, 102, 102);">    write_head</span>(2);
            <span style="color: rgb(102, 51, 51);">#/0.5/s2;</span>
            <span style="color: rgb(102, 51, 51);">#SIT2(0.1)</span> 
            <span style="color: rgb(51, 102, 102);">    send_sms</span>(CAN_PC,”9209170680”,”Тестовое сообщение”);
            <span style="color: rgb(102, 51, 51);">#/s3;</span>
            <span style="color: rgb(102, 51, 51);">#SIT3(0.1)</span>
            <span style="color: rgb(102, 51, 51);">#/R;</span>
        </pre>
        <p>
            Если в качестве буфера для текста смс используется массив в оперативной памяти, то возможна вставка значений переменных типа  unsigned char,unsigned short и unsigned long в текст сообщения с помощью функции print_long_buf().<br>
            <span style="font-weight: bold; font-style: italic; color:blue">unsigned char print_long_buf(unsigned long val,unsigned char* buf);</span>
            val  - значение переменной<br>
            buf – указатель на элемент буфера, с которого начнётся вывод переменной<br>
            Функция возвращает количество затраченных знакомест в буфере<br>
            Старшие незначащие символы при этом не выводятся. Число 23, например, будет выведено как «23», а не «00023». Функция вернёт при этом значение 2, так как для вывода числа необходимы 2 символа.<br>
        </p>
        <pre>
            unsigned char my_buf[] = "Текущее время:  :  :              ";
            #SIT2(0.1)
                print_long_buf(times.hour,&my_buf[14]);
                print_long_buf(times.min,&my_buf[17]);
                print_long_buf(times.sec,&my_buf[20]);
                write_head(CAN_PC);
            #/0.5/s3;
            #SIT3(0.1)
                send_sms(CAN_PC,"9209170680",my_buf);
            #/s4;
            #SIT4(0.1)
            #/R;
        </pre>
        <p>В этом примере в сообщении высылается текущее время контроллера.</p>
        <p>
            <h3>Приём входящих текстовых сообщений (работает только на каналах PC и PR)</h3>
            В Релкон встроен ряд функций, позволяющих принимать и обрабатывать входящие SMS. Приём сообщений имеет несколько особенностей. Во первых, доступны только сообщения, начинающиеся с шестизначного пароля, известного программе. Для установки пароля необходимо воспользоваться функцией set_password().<br>
            <span style="font-weight: bold; font-style: italic; color:blue">void set_password(unsigned char* ptr);</span><br>
            ptr – указатель на буфер, содержащий пароль<br>
            Пример вызова функции: set_password("Релкон");<br>
            Пароль достаточно указать один раз при старте программы. Другой особенностью является необходимость принудительно очистить несколько ячеек памяти модема для приёма SMS, поскольку в противном случае может возникнуть ситуация когда приходящим сообщениям некуда будет сохраняться. Для этого во время инициализации модема необходимо выполнить несколько команд <b>AT+CMGD</b> (более подробно смотри справку по AT командам). Третий важный момент — необходимо разрешить индикацию входящих сообщений командой  <b>AT+CNMI=2,1</b>. При приёме SMS модем будет выдавать сообщение вида <b>+CMTI:</b>. Отслеживая его можно приступить к обработке принятого сообщения.
        </p>
        <p>
            Обработка SMS проходит в несколько этапов:<br><br>
            <b>1) Получение номера ячейки памяти, содержащей sms<br></b>
            <code>
            unsigned char sms_i;<br>
                sms_i = <span style="font-weight: bold; font-style: italic; color:blue">get_index(CAN_PC);</span><br>
            </code><br>
            <b>2) Очистка приёмного буфера и чтение SMS<br></b>
            <code>
                <span style="font-weight: bold; font-style: italic; color:blue">clear_buf(CAN_PC);</span><br>
                <span style="font-weight: bold; font-style: italic; color:blue">read_sms(CAN_PC,sms_i);</span><br>
            </code>
            Параметр sms_i содержит индекс ячейки памяти, полученный ранее с помощью команды get index().<br><br>
            <b>3) Сохранение текста сообщения в памяти контроллера<br></b>
            <code>
                #define SMS_L (70-6)<br>
                unsigned char sms_txt[SMS_L];<br>
                for(j=0;j&lt;SMS_L;j++) sms_txt[j]=0;<br>
                j=<span style="font-weight: bold; font-style: italic; color:blue">get_sms_text(CAN_PC,sms_txt,SMS_L);</span><br>
            </code><br>
            Шесть символов зарезервированы под пароль. Поэтому под сообщение выделяется 70-6 символов. Функция get_sms_text сохраняет сообщение в буфер sms_txt и возвращает количество прочитанных символов.<br><br>
            Для поиска подстроки в сохранённом буфере можно воспользоваться функцией find.<br>
            <span style="font-weight: bold; font-style: italic; color:blue">unsigned char find(unsigned char* str,unsigned char* buf, unsigned short l);</span><br>
            Ищет подстроку str  в первых l символах буфера buf.
            <pre>
    j=get_sms_text(CAN_PC,sms_txt,SMS_L);
    if(j)
    {
        if(find("вкл",sms_txt,SMS_L)) #OUT0.0=1;
        if(find("выкл",sms_txt,SMS_L)) #OUT0.0=0;
    }
            </pre><br>
            <b>4) Очистка приёмного буфера и удаление SMS из памяти модема</b><br>
            <code>
                clear_buf(CAN_PC);<br>
                <span style="font-weight: bold; font-style: italic; color:blue">del_sms(CAN_PC,sms_i);</span><br>
            </code>
            Параметр sms_i содержит индекс ячейки памяти, полученный ранее с помощью команды get index().<br>
        </p>
        <p>Ниже приведён пример программы, иллюстрирующий разбор входящих сообщений.</p>
        <pre>
#DATA
// Присвоение переменных программы.
    #define SMS_L (70-6)
    unsigned char modem_stat=0;
    unsigned short modem_tmr=0;
    unsigned char sms_i;
    unsigned char j;
    unsigned char sms_txt[SMS_L];
#INIT
#STARTp0;
//-----Начало программы.
#PROCESS 0
#SIT1(0.1)
    switch(modem_stat)
    {
        case 0:
            send_str(CAN_PC,"AT\r\n");
            modem_stat++;break;
        case 1:
            if(search(CAN_PC,"OK")) {modem_stat++;modem_tmr=0;}
            else modem_stat=0;break;
        case 2:
            send_str(CAN_PC,"AT+CREG?\r\n");
            modem_stat++;break; 
        case 3:
            if((search(CAN_PC,",1"))||(search(CAN_PC,",5"))) 
            {modem_stat++;} else modem_stat=2;break;
        case 4:
            send_str(CAN_PC,"AT+CMGD=1\r\n");
            modem_stat++;modem_tmr=0;break;
        case 5:
            modem_tmr++;
            if(modem_tmr>=10) {modem_tmr=0;modem_stat++;} 
            break;
        case 6:
            send_str(CAN_PC,"AT+CMGD=2\r\n");
            modem_stat++;modem_tmr=0;break;
        case 7:
            modem_tmr++;
            if(modem_tmr>=10) {modem_tmr=0;modem_stat++;} 
            break;
        case 8:
            send_str(CAN_PC,"AT+CMGD=3\r\n");
            modem_stat++;modem_tmr=0;break;
        case 9:
            modem_tmr++;
            if(modem_tmr>=10) {modem_tmr=0;modem_stat++;} 
            break;
        case 10:
            send_str(CAN_PC,"AT+CNMI=2,1\r\n");
            modem_stat++;break;
        case 11:
            if(search(CAN_PC,"OK")) {modem_stat++;}
            else modem_stat--;break;
        case 12:break; 
    }
    if(modem_stat==12) 
    {
        set_password("Релкон");
        #/1.0/s2;
    }
    #/R;

#SIT2(0.1)
    if(search(CAN_PC,"+CMTI:")) {#/0.5/s3;}
#/R;

#SIT3(0.1)
    sms_i=get_index(CAN_PC);
    clear_buf(CAN_PC);
    read_sms(CAN_PC,sms_i);
#/3.0/s4;

#SIT4(0.1)
    for(j=0;j&lt;SMS_L;j++) sms_txt[j]=0;
    j=get_sms_text(CAN_PC,sms_txt,SMS_L);
    if(j)
    {
        if(find("вкл",sms_txt,SMS_L)) #OUT0.0 = 1;
        if(find("выкл",sms_txt,SMS_L)) #OUT0.0 = 0;
    }
    clear_buf(CAN_PC);
    del_sms(CAN_PC,sms_i);
#/s2;
        </pre>
        <p>
            Кроме обработки содержания смс возможно получение номера отправителя. Это позволяет сделать функция <span style="font-weight: bold; font-style: italic; color:blue">get_number(unsigned char* in_sms)</span>, где in_sms – буфер размерностью не менее 11 байт, в котором будет сохранён номер. Вызывать get_number() необходимо после вызова функции get_sms_text(). 
        </p>

        <p><span style="font-weight: bold;"><a name="a19"></a>МЕЖКОНТРОЛЛЕРНЫЙ ОБМЕН</span></p>
        <p>
            Релкон позволяет организовать межконтроллерный обмен по интерфейсу <span style="font-weight: bold;">RS485</span> в группе до 8 контроллеров. Управляет обменом один контроллер. Он является инициатором обмена (мастером-ведущим). Остальные контроллеры являются ведомыми (отвечают на запросы мастера). Физически для работы обмена должны быть объединены интерфейс MB мастера (при условии, что его программа не опрашивает дополнительные модули ввода/вывода) с любыми каналами ведомых контроллеров1 (при условии, что каналы настроены на протокол RKBIN и скорость равна 115200 бит/с). Обмен данных выполняется буферами по 64 байта. Мастер может получать буфера от всех контроллеров. Ведомые же контроллеры могут работать только с данными буфера мастера. При максимальном количестве контроллеров полный системный цикл обмена между всеми контроллерами не должен превышать одной секунды. При двух контроллерах — 0,1 секунды.
        </p>
        <p>Ниже приведены программы двух контроллеров, обменивающихся между собой данными АЦП.</p>
        <p><span style="font-style: italic;">МАСТЕР:</span></p>
        <pre>
            <span style="color: rgb(102, 51, 51);">#DATA</span>
            <span style="color: rgb(0, 0, 153);">   unsigned short</span> t_plc2;  
            <span style="color: rgb(0, 0, 153);">   unsigned char</span> avar_change;  
            <span style="color: rgb(102, 51, 51);">#INIT</span>
            <span style="color: rgb(102, 51, 51);">#STARTp0;</span>  
            <span style="color: rgb(0, 102, 0);">//-----Начало программы.</span>
            <span style="color: rgb(102, 51, 51);">#PROCESS 0</span>
            <span style="color: rgb(102, 51, 51);">#SIT1(0.1)</span>   
            <span style="color: rgb(153, 0, 0);">   EXCHANGE</span>=1; <span style="color: rgb(153, 0, 0);">START_P2</span>=1;  
            <span style="color: rgb(102, 51, 51);">#/0.5/s2;</span>
            <span style="color: rgb(102, 51, 51);">#SIT2(0.1)</span>  
            <span style="color: rgb(153, 0, 0);">   TX_2</span>[0]=<span style="color: rgb(102, 51, 51);">ADC1</span>&gt;&gt;8; <span style="color: rgb(0, 102, 0);">// старший байт</span>
            <span style="color: rgb(153, 0, 0);">   TX_2</span>[1]=<span style="color: rgb(102, 51, 51);">ADC1</span> & 0xFF; <span style="color: rgb(0, 102, 0);">// младший байт</span>  
            <span>   t_plc2</span> = (((<span style="color: rgb(0, 0, 153);">unsigned short</span>) <span style="color: rgb(153, 0, 0);">RX_2</span>[0])&lt;&lt;8) | <span style="color: rgb(153, 0, 0);">RX_2</span>[1];<span style="color: rgb(0, 102, 0);">// принятые данные</span>
            <span style="color: rgb(0, 0, 153);">   if</span>(<span style="color: rgb(153, 0, 0);">ERR2</span>==255) avar_change=1; <span style="color: rgb(0, 0, 153);">else</span> avar_change=0; <span style="color: rgb(0, 102, 0);">// контроль ошибок связи</span>
            <span style="color: rgb(102, 51, 51);">#/R;</span>
        </pre>
        <p>
            Переменные <span style="color: rgb(102, 51, 51);">EXCHANGE</span> и <span style="color: rgb(102, 51, 51);">START_P2</span> системные. <span style="color: rgb(102, 51, 51);">EXCHANGE</span> является флагом, определяющим, что контроллер является мастером. <span style="color: rgb(102, 51, 51);">START_P2</span> запускает автоматический обмен данными между мастером и контроллером со вторым сетевым адресом. <span style="color: rgb(102, 51, 51);">TX_2</span> – буфер для передачи данных второму контроллеру. <span style="color: rgb(102, 51, 51);">RX_2</span> – буфер для приёма данных от второго контроллера (для третьего контроллера будут переменные <span style="color: rgb(102, 51, 51);">START_P3</span>, <span style="color: rgb(102, 51, 51);">TX_3</span>[0...63], <span style="color: rgb(102, 51, 51);">RX_3</span>[0...63] и т. д. ). Переменная <span style="color: rgb(102, 51, 51);">ERR2</span> (однобайтовая) служит для контроля связи со вторым контроллером. Перед каждым запросом ко второму контроллеру эта переменная автоматически увеличивается на единицу, но если получен корректный ответ сбрасывается в ноль. Для ведомого контроллера программа упрощается. Там достаточно работать только с буферами данных.
        </p>
        <p>
            Если возникает необходимость опрашивать контроллер с сетевым адресом выходящим за пределы диапазона [1..8] то можно воспользоваться функцией set_exchange_net_addr(unsigned char plcNum, unsigned char netAddr). plcNum - это внутренний индекс контроллера с которым работает программа и связанный с буферами TX, RX и переменной ERR, а netAddr - реальный адрес контроллера. Например при вызове функции set_exchange_net_addr(2,18) мы говорим, что реальный адрес контроллера - 18, а данные связанные с ним хранятся в массивах TX_2, RX_2 и переменной ERR2. 
        </p>
        <p><span style="font-style: italic;">ВЕДОМЫЙ:</span></p>
        <pre>
            <span style="color: rgb(102, 51, 51);">#DATA</span>
            <span style="color: rgb(0, 0, 153);">   unsigned short</span> t_plc1;
            <span style="color: rgb(102, 51, 51);">#INIT</span>
            <span style="color: rgb(102, 51, 51);">#STARTp0;</span>   
            <span style="color: rgb(0, 102, 0);">//-----Начало программы.</span>  
            <span style="color: rgb(102, 51, 51);">#PROCESS 0</span>
            <span style="color: rgb(102, 51, 51);">#SIT1(0.1)</span>
            <span style="color: rgb(153, 0, 0);">   TX</span>[0]=<span style="color: rgb(102, 51, 51);">ADC1</span>&gt;&gt;8; <span style="color: rgb(0, 102, 0);">// старший байт</span>  
            <span style="color: rgb(153, 0, 0);">   TX</span>[1]=<span style="color: rgb(102, 51, 51);">ADC1</span> & 0xFF;<span style="color: rgb(0, 102, 0);"> // младший байт</span> 
            <span>   t_plc1</span> = (((<span style="color: rgb(0, 0, 153);">unsigned short</span>) <span style="color: rgb(153, 0, 0);">RX</span>[0])&lt;&lt;8) | <span style="color: rgb(153, 0, 0);">RX</span>[1]; <span style="color: rgb(0, 102, 0);">// принятые данные</span> 
            <span style="color: rgb(0, 102, 0);">   // контроль ошибок связи</span> 
            <span style="color: rgb(153, 0, 0);">   ERR1</span>++;<span style="color: rgb(0, 0, 153);">if</span>(<span style="color: rgb(153, 0, 0);">ERR1</span>>=50) {<span style="color: rgb(153, 0, 0);">ERR1</span>=50; avar_change=1;} <span style="color: rgb(0, 0, 153);">else</span> avar_change=0;   
            <span style="color: rgb(102, 51, 51);">#/R;</span>
        </pre>
        <p>
            В отличие от мастера (в данном примере сетевой адрес мастера - 1) счётчик <span style="color: rgb(102, 51, 51);">ERR1</span> автоматически не увеличивается. Это необходимо делать программно (в случае если необходим контроль связи). При каждом корректном запросе от первого контроллера он сбрасывается в ноль. В данном примере ошибка возникнет если в течение 5 секунд не произошло обмена данными. Если бы сетевой адрес мастера был равен 2, то вместо переменной <span style="color: rgb(102, 51, 51);">ERR1</span> следовало бы использовать <span style="color: rgb(102, 51, 51);">ERR2</span> и т. п. Следует помнить, что внутри одной сети не может быть более 8 контроллеров(включая мастера), а их сетевые адреса могут быть только в диапазоне от 1 до 8. Также не допускается двух мастеров в одной сети. Хотя контроллер, являющийся мастером в одной сети, одновременно может быть ведомым для другой сети. Но ведомый контроллер не может опрашиваться несколькими ведущими.
        </p>
        <p><span style="font-weight: bold;"><a name="a19"></a>МЕЖКОНТРОЛЛЕРНЫЙ ОБМЕН ПО ETHERNET СЕТИ</span></p>
        <p>
            Если контроллеры поддерживают Ethernet интерфейс то возможен обмен данными между ними по протоколу UDP. Этот вариант допускает, что каждый контроллер в сети может быть мастером. Программно обмен организуется с помощью системной структуры <span style="color: blue">udp_request</span> и функции <span style="color:red">udp_cmd</span>. Структура udp_request содержит следующие поля:<br>
            <li>cmd - код команды (может принимать значения UDP_RD_USER(чтение памяти пользователя), UDP_WR_USER(запись памяти пользователя) )</li>
            <li>ip[4] - массив из 4 байт типа unsigned char с ip адресом устройства которому адресована команда</li>
            <li>addr - переменная типа unsigned short с адресом начала блока памяти для чтения или записи</li>
            <li>cnt - переменная типа unsigned short с количеством байт для чтения записи</li>
            <li>unsigned char *rx - указатель на буфер в котором будут сохранены считанные данные (необходимо задать при команде чтения). Размер буфера должен быть больше или равен значению параметра cnt</li>
            <li>unsigned char *tx - указатель на буфер в котором хранятся данные для записи (необходимо задать при команде записи). Размер буфера должен быть больше или равен значению параметра cnt</li>
            <li>result - при вызове функции udp_cmd сбрасывается в 0, при получении ответа от контроллера устанавливается в 1</li>
            <br>
            У функции udp_cmd только один параметр - указатель на структуру типа udp_request с данными запроса. Вызов этой функции приводит к отправке в сеть соответствующего запросу пакета данных. В случае корректного ответа переменная result примет значение - 1, а в буфер, на который ссылается указатель rx, будут сохранены принятые данные (при использовании команды чтения).
            <br>Между вызовами функций udp_cmd рекомендуется выдерживать паузу, достаточную для гарантированной отправки пакета в сеть. Ниже приведён пример программы опроса двух контроллеров. С одного контроллера читается блок из 10 байт. В другой контроллер пишется блок такого же размера. Следует помнить, что память пользователя ограничена размером 256 байт (можно считывать данные в диапазоне адресов от 0 до 255).
            <img src="eth_master_example.png" alt="пример программы">
            <br>Обмен данными между контроллерами возможен только внутри подсети.
        </p>
        <p><span style="font-weight: bold;"><a name="a20"></a>ИНТЕРФЕЙСНЫЕ ПРОТОКОЛЫ</span></p>
        <p>
            Контроллеры поддерживают протоколы RKBIN, RKASCII и часть функций <span style="font-weight: bold; font-style: italic;">Modbus RTU</span> и <span style="font-weight: bold; font-style: italic;">Modbus ASCII</span>. Связь с контроллером выполняется по интерфейсу <span style="font-weight: bold;">RS485</span> на скоростях 4800, 9600, 19200, 38400, 57600, 115200 бит/с. Формат передачи данных <span style="font-weight: bold;">8N1</span> (8 бит данных, без бита чётности, 1 стоп бит). Все контроллеры работают в режиме ведомого (SLAVE) по полудуплексной схеме.
        </p>
        <p>
            Если в контроллере аппаратно поддерживается <span style="font-weight: bold;">Ethernet</span> сеть, возможен опрос контроллера по протоколу UDP (порт 12144) всеми командами протокола <span style="font-weight: bold; font-style: italic;">RKBIN</span> или по протоколу <span style="font-weight: bold; font-style: italic;">Modbus TCP</span> (порт 502). IP и MAC адрес задаются в настройках контроллера в среде программирования. В случае если программа контроллера не обращается к модулям ввода/вывода серии MATCHBOX освободившийся канал RS485 может использоваться для связи с контроллером по протоколу MODBUS RTU на скорости 19200 бит/с. Но здесь имеются ограничения: не доступны команды работы с FRAM памятью и часами; объём данных в посылке запроса или ответа не может превышать 128 байт.
        </p>
        <p><span style="font-weight: bold;"><a name="a21"></a>Протокол RKBIN</span></p>
        <p>
            В бинарном режиме сообщение начинается с интервала тишины, равного времени передачи 3.5 символов при данной скорости в сети. Первым полем передается адрес устройства. Потом следует код и параметры команды. Заканчивается запрос двумя байтами контрольной суммы. Вслед за последним передаваемым символом также следует интервал тишины продолжительностью не менее 3.5 символов. Сообщение передается непрерывно. Если интервал тишины продолжительностью 1.5 байта возник во время передачи фрейма, принимающее устройство заканчивает прием и следующий байт будет воспринят как начало нового сообщения.
        </p>
            Контроллер ответит на сообщение только в случае если совпадают протокол, скорость и сетевой адрес запроса и контроллера; контрольная сумма верно рассчитана и получена без искажений; контроллер поддерживает запрашиваемую команду. Если сетевой адрес контроллера неизвестен, в запросе можно указать его, равным нулю (общесетевой запрос). Но необходимо помнить, что в этом случае для избежания конфликта в сети может 
        <p>Поддерживаемые команды:</p>
        <p>
            <span style="font-style: italic;">Adr</span> – сетевой адрес контроллера<br>    <span style="font-style: italic;">CRCH, CRCL</span> – старший и младший байты контрольной суммы<br>    <span style="font-style: italic;">A, AH, AL</span> – адрес (<span style="font-style: italic;">AH</span> – старший, <span style="font-style: italic;">AL</span> - младший байт)<br>    <span style="font-style: italic;">N, NH, NL</span> – количество байт
        </p>
        <p>
            <span style="font-weight: bold;">1) Запрос версии операционной системы контроллера (0xA0)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xA0 CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xA0 0x01 0xC8</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xA0 Версия CRCH CRCL</span><br>Версия передаётся в виде массива байт текстовой строки.
        </p>
        <p>
            <span style="font-weight: bold;">2) Запрос версии процессора (0xA1)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xA1 CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xA1 0xC0 0x08</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xA1 Версия CRCH CRCL</span><br>Версия передаётся в виде массива байт текстовой строки.
        </p>
        <p>
            <span style="font-weight: bold;">3) Чтение состояния входов/выходов (0xB0)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xB0 AH AL NH NL CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xB0 0x00 0x00 0x00 0x04</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br>Количество запрашиваемых байт не должно превышать 500. В противном случае будет выдано сообщение об ошибке: Adr 0xFF CRCH CRCL. Ниже представлено распределение данных в зависимости от адресов.
        </p>
        <p>
            <table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2"><tbody><tr><td><br>Диапазон адресов</td><td>Данные</td></tr><tr><td>0x00...0x05</td><td>IN0,IN1,IN2,IN3,DIN4,DIN5</td></tr><tr><td>0x06...0x0B </td><td>OUT0,OUT1,OUT2,OUT3,DOUT4,DOUT5</td></tr><tr><td>0x0C...0x1B </td><td>ADC1...ADC8</td></tr><tr><td>0x1C...0x23</td><td>DAC1...DAC4</td></tr><tr><td>0x24...0x43</td><td>IN4...IN35</td></tr><tr><td>0x44...0x63 </td><td>OUT4...OUT35</td></tr><tr><td>0x64...0x163 </td><td>ADC9...ADC136</td></tr><tr><td>0x164...0x1E3 </td><td>DAC5...DAC68</td></tr></tbody></table>
        </p>
        <p>
            <span style="font-weight: bold;">4) Запись состояния входов/выходов (0xB1)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xB1 AH AL NH NL Данные CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xB1 0x00 0x06 0x00 0x01 0x0F 0x41 0xFD</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xB1 CRCH CRCL</span><br>Объём записываемых данных должен соответствовать количеству, указанному в запросе (NH, NL), но не может превышать 500 байт. В противном случае контроллер выдаст сообщение об ошибке: <span style="font-weight: bold;">Adr 0xFF CRCH CRCL</span>.
        </p>
        <p>
            <span style="font-weight: bold;">5) Чтение памяти пользователя (0xD0)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD0 A N CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD0 0x00 0x40 0x00 0x2D</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br><br>
        </p>
        <p>
            <span style="font-weight: bold;">6) Запись памяти пользователя (0xE0)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xE0 A N Данные CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xE0 0x00 0x02 0x01 0x02 0xA0 0x5C</span><br style="font-style: italic;">Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br><br>
        </p>
        <p>
            <span style="font-weight: bold;">7) Чтение времени (0xD1)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD1 A N CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD1 0x00 0x06 0xD0 0x1F</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br><br>Ниже в таблице представлена карта памяти временных данных.
        </p>
        <p>
            <table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2"><tbody><tr><td>Адрес</td><td>Данные</td></tr><tr><td>0x00</td><td>Секунды (единицы в младшей тетраде, десятки в старшей)</td></tr><tr><td>0x01</td><td>Минуты (единицы в младшей тетраде, десятки в старшей)</td></tr><tr><td>0x02</td><td>Часы (единицы в младшей тетраде, десятки в старшей)</td></tr><tr><td>0x03</td><td>не используется</td></tr><tr><td>0x04</td><td>Число/дата (единицы в младшей тетраде, десятки в старшей)</td></tr><tr><td>0x05</td><td>Номер месяца (единицы в младшей тетраде, десятки в старшей)</td></tr><tr><td>0x06</td><td>Год (единицы в младшей тетраде, десятки в старшей)</td></tr></tbody></table>
        </p>
        <p>
            <span style="font-weight: bold;">8) Запись времени (0xE1)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xE1 A N Данные CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xE1 0x01 0x01 0x05 0x82 0x6F</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xE1 CRCH CRCL</span><br><br>Объём записываемых данных должен соответствовать количеству, указанному в запросе (N), но не может превышать 128 байт. В противном случае контроллер выдаст сообщение об ошибке: <span style="font-weight: bold;">Adr 0xFF CRCH CRCL</span>. Запись данных по адресам выше 0x06 не имеет смысла.
        </p>
        <p>
            <span style="font-weight: bold;">9) Чтение энергонезависимой FRAM памяти (0xD3)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD3 AH AL NH NL CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD3 0x00 0x00 0x00 0x08 0x84 0x0F</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br>Количество байт для чтения (NH, NL) не должно превышать 128. Объём FRAM памяти ограничен размером 32 кБайт. Поэтому адрес должен быть в диапазоне 0x0000...0x7FFF.
        </p>
        <p>
            <span style="font-weight: bold;">10) Чтение заводских установок (0xD6)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD6 AH AL NH NL CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD6 0x00 0x00 0x00 0x0A 0xС9 0xСЕ</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br>Количество байт для чтения (NH, NL) не должно превышать 128. Объём памяти ограничен размером 1024 Байт. Чтение выполняется из оперативной памяти контроллера.
        </p>
        <p>
            <span style="font-weight: bold;">11) Запись энергонезависимой FRAM памяти (0xE3)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xE3 AH AL NH NL Данные CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xE3 0x00 0x00 0x00 0x01 0xAA 0x8C 0xBC</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xE3 CRCH CRCL</span><br>Объём записываемых данных должен соответствовать количеству, указанному в запросе (NH, NL), но не может превышать 128 байт. В противном случае контроллер выдаст сообщение об ошибке: <span style="font-weight: bold;">Adr 0xFF CRCH CRCL.</span><br>Адреса, начиная с 0x7B00 и выше используются операционной системой контроллера, поэтому использовать их нужно крайне осторожно.
        </p>
        <p>
            <span style="font-weight: bold;">12) Чтение оперативной памяти (0xD4)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD4 AH AL NH NL CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD4 0x01 0x00 0x01 0x90 0x06 0x44</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br>В одном запросе допускается считывать не более 500 байт.
        </p>
        <p>
            <span style="font-weight: bold;">13) Запись оперативной памяти (0xE4)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xE4 AH AL NH NL Данные CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xE4 0x25 0x80 0x00 0x02 0xFF 0xFF 0x42 0x9E</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xE4 CRCH CRCL</span><br>Адреса переменных для записи берутся из карты памяти проекта. Неосторожное обращение с командой 0xE4 (неверный адрес или объём данных) могут разрушить работу операционной системы контроллера. Объём данных не может превышать 500 байт.
        </p>
        <p>
            <span style="font-weight: bold;">14) Запись заводских установок (0xE6)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xE6 AH AL NH NL Данные CRCH CRCL</span><br>Пример запроса:<span style="font-style: italic;"> 0x00 0xE6 0x00 0x00 0x00 0x02 0x01 0x02 0xЕ6 0x54</span><br>Формат ответа: <span style="font-weight: bold;">Adr 0xE 6 CRCH CRCL</span><br>Данные пишутся одновременно как в энергонезависимую память, так и в зеркало переменных в оперативной памяти. Объём данных не может превышать 128 байт.
        </p>
        <p>
            <span style="font-weight: bold;">15) Чтение flash памяти программы (0xD5)</span><br>Формат запроса: <span style="font-weight: bold;">Adr 0xD5 AH1 AL1 AH2 AL2 NH NL CRCH CRCL</span><br>Пример запроса: <span style="font-style: italic;">0x00 0xD5 0x00 0x01 0x05 0x3D 0x00 0x0F 0x28 0x02</span><br>Формат ответа: <span style="font-weight: bold;">Adr Данные CRCH CRCL</span><br>Допускается чтение не более 500 байт.
        </p>
        <p><span style="font-weight: bold;"><a name="a22"></a>Протокол RKASCII</span></p>
        <p>
            Используется таблица символов ASCII, где каждому символу соответствует два байта данных. ASCII режим использует в два раза больше символов, чем бинарный, но допускает передачу пакета частями. Это может быть важно в случаях, где при опросе возможны разрывы пакетов данных (например работа по GSM каналу). Каждая посылка обрамляется символами начала и конца. Для запроса это 0x24 и 0x0D, а для ответа 0x21 и 0x0D. Для сравнения ниже приведён запрос версии операционной системы по бинарному и по ASCII протоколам:
        </p>
        <p>
            <span style="font-weight: bold;">0x01 0xA0 0x00 0x58</span> – RKBIN<br><span style="font-weight: bold;">'$' '0' '1' 'A' '0' '0' '0' '5' '8' '\r'</span> – RKASCII в символьном виде<br><span style="font-weight: bold;">0x24 0x30 0x31 0x41 0x30 0x30 0x30 0x35 0x38 0x0D</span> — RKASCII в hex виде
        </p>
        <p>
            Протокол RKASCII поддерживает те же команды, что и протокол RKBIN, за исключением команд протокола Modbus. Отличается также формат ошибки в случае неверного запроса: '?' 'A1' 'A2' '\r', где A1 и A2 — ASCII символы, определяющие сетевой адрес контроллера.
        </p>
        <p style="text-align: center;"><span style="font-weight: bold;">Общий перечень команд RKBIN и RKASCII</span></p>
        <p>
            <table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2">
                <tbody>
                    <tr><td>Код</td><td> Функция</td><td> Запрос</td><td> Ответ</td><td> Nmax (BIN)</td><td>Nmax (ASCII)</td></tr>
                    <tr><td>0xA0</td><td>Запрос версии ОС</td><td>Adr Cmd CRCH CRCL</td><td>Adr Cmd DATA CRCH CRCL</td><td></td><td></td></tr>
                    <tr><td>0xA1</td><td>Запрос версии  процессора</td><td>Adr Cmd CRCH CRCL</td><td>Adr Cmd DATA CRCH CRCL</td><td></td><td></td></tr>
                    <tr><td>0xB0</td><td>Чтение входов/выходов</td><td>Adr Cmd AH AL NH NL CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>500</td><td>250</td></tr>
                    <tr><td>0xB1 </td><td>Запись входов/выходов</td><td>Adr Cmd AH AL NH NL DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>500</td><td>250</td></tr>
                    <tr><td>0xD0</td><td>Чтение памяти пользователя</td><td>Adr Cmd A N CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>-</td><td>128</td></tr>
                    <tr><td>0xD1</td><td>Чтение времени</td><td>Adr Cmd A N CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>128</td><td>128</td></tr>
                    <tr><td>0xD3 </td><td>Чтение FRAM памяти</td><td>Adr Cmd AH AL NH NL CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>128</td><td>128</td></tr>
                    <tr><td>0xD4</td><td>Чтение оперативной памяти</td><td>Adr Cmd AH AL NH NL CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>500</td><td>250</td></tr>
                    <tr><td>0xD6</td><td>Чтение зав. уставок</td><td>Adr Cmd AH AL NH NL CRCH CRCL</td><td>Adr DATA CRCH CRCL</td><td>128</td><td>128</td></tr>
                    <tr><td>0xD5</td><td>Чтение flash памяти</td><td>Adr Cmd A1H A1L A2H A2L NH NL CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>500</td><td>250</td></tr>
                    <tr><td>0xE0 </td><td>Запись памяти пользователя</td><td>Adr Cmd A N DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>-</td><td>128</td></tr>
                    <tr><td>0xE1</td><td>Запись времени</td><td>Adr Cmd A N DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>128</td><td>128</td></tr>
                    <tr><td>0xE3</td><td>Запись FRAM памяти</td><td>Adr Cmd AH AL NH NL DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>128</td><td>128</td></tr>
                    <tr><td>0xE4</td><td>Запись оперативной памяти</td><td>Adr Cmd AH AL NH NL DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>500</td><td>250</td></tr>
                    <tr><td>0xE6</td><td>Запись зав. уставок</td><td>Adr Cmd AH AL NH NL DATA CRCH CRCL</td><td>Adr Cmd CRCH CRCL</td><td>128</td><td>128</td></tr>
                </tbody>
            </table>
        </p>
        <p>
            <span style="font-weight: bold;">Adr </span>– сетевой адрес контроллера<br><span style="font-weight: bold;">Cmd </span>— код команды<br><span style="font-weight: bold;">A, AH, AL, A1H, A1L, A2H, A2L</span> – параметры адреса (H – старший байт, L – младший байт)<br><span style="font-weight: bold;">N, NH, NL</span> – количество байт<br><span style="font-weight: bold;">DATA</span> – массив данных<br><span style="font-weight: bold;">CRCH, CRCL</span> – контрольная сумма
        </p>
        <p><span style="font-weight: bold;"><a name="a23"></a>Поддерживаемые функции Modbus RTU, Modbus ASCII и Modbus TCP</span></p>
        <p>
            При настройке интерфейса контроллера на протокол RKBIN доступна часть функций протокола Modbus RTU:
        </p>
        <p>
            <span style="font-weight: bold;">1) Read Coils (0x01)</span> - битовое чтение дискретных выходов<br>Область дискретных выходов разбивается на 2 части. Одна описывает состояние собственных выходов контроллера (6 регистров по 8 выходов - OUT0, OUT1, OUT2, OUT3, DOUT4, DOUT5), вторая служит для работы с внешними выходами в виде модулей расширения (OUT4...OUT35). Первая область хранится по адресам 0x00...0x2F, вторая, начиная с адреса 0x30. Ниже приведены примеры адресов некоторых выходов:
        </p>
        <p>
            #OUT0.0 – 0x00<br>
            #OUT0.7 – 0x07<br>
            #OUT1.0 – 0x08<br>
            #OUT4.0 – 0x30<br>
            #OUT4.3 – 0x33<br>
            #OUT5.1 – 0x34<br><br>
            Допустимые адреса ограничены диапазоном [0x00...0xAF].
        </p>
        <p>
            <span style="font-weight: bold;">2) Read Discrete Inputs (0x02) </span>— битовое чтение дискретных входов<br>Аналогична команде 0x01, за тем исключением, что вместо переменных OUT0, OUT1, OUT2, OUT3, DOUT4, DOUT5, OUT4...OUT35 обращение выполняется к переменным IN0, IN1, IN2, IN3, DIN4, DIN5, IN4...IN35 соответственно.
        </p>
        <p>
            <span style="font-weight: bold;">3) Read Holding Registers (0x03)</span> – чтение памяти пользователя<br>Допустимые адреса ограничены диапазоном [0x00...0x7F].
        </p>
        <p>
            <span style="font-weight: bold;">4) Read Input Registers (0x04)</span> – чтение данных АЦП<br>В диапазоне адресов 0x00...0x0F выдаёт значение переменных ADC1...ADC8.
        </p>
        <p>
            <span style="font-weight: bold;">5) Write Single Coil (0x05)</span><br style="font-weight: bold;">
        </p>
        <p>
            <span style="font-weight: bold;">6) Write Single Register (0x06)</span>
        </p>
        <p>
            <span style="font-weight: bold;">7) Write Multiple Coils (0x0F)</span>
        </p>
        <p>
            <span style="font-weight: bold;">8) Write Multiple Registers (0x10)</span>
        </p>
        <br>
        <p><span style="font-weight: bold;"><a name="a24"></a>Алгоритм расчёта контрольной суммы</span></p>
        <p>Ниже приведён пример реализации расчёта контрольной суммы на языке Си табличным методом.</p>
        <pre>
            const unsigned char crc16H[256]={
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
            0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
            0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
            0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00,
            0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
            0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01,
            0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
            0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40} ;
            
            
            const unsigned char crc16L[256]={
            0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D,
            0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB,
            0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2,
            0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37,
            0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 
            0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 
            0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 
            0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 
            0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 
            0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 
            0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 
            0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 
            0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 
            0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40} ;
            
            unsigned int GetCRC16 (unsigned char* ptr,unsigned int cnt)
            {
                unsigned char i;
                unsigned char CRCH=0xFF,CRCL=0xFF;
                while(cnt--)
                {
                    i=CRCH ^ *ptr++;
                    CRCH=CRCL ^ crc16H[i];
                    CRCL=crc16L[i];
                }
                return (((unsigned int)CRCH&lt;&lt;8)|CRCL);
            }
        </pre>
    </div>
</body>
</html>